├── .gitignore ├── src ├── etc │ └── header.txt ├── test │ ├── projects │ │ ├── example-project │ │ │ ├── module2 │ │ │ │ └── pom.xml │ │ │ ├── module5 │ │ │ │ └── pom.xml │ │ │ ├── module3 │ │ │ │ └── pom.xml │ │ │ ├── module4 │ │ │ │ └── pom.xml │ │ │ └── module1 │ │ │ │ └── pom.xml │ │ ├── issue-2 │ │ │ ├── jar-module │ │ │ │ └── pom.xml │ │ │ ├── pom-module │ │ │ │ └── pom.xml │ │ │ └── pom.xml │ │ ├── issue-53 │ │ │ ├── module1 │ │ │ │ └── pom.xml │ │ │ └── pom.xml │ │ ├── plugins │ │ │ └── pom.xml │ │ ├── warn-only │ │ │ └── pom.xml │ │ ├── simple-project │ │ │ └── pom.xml │ │ └── issue-23 │ │ │ └── pom.xml │ └── java │ │ └── com │ │ └── github │ │ └── ferstl │ │ └── maven │ │ └── pomenforcers │ │ ├── model │ │ ├── DependencyElementTest.java │ │ ├── ModelTest.java │ │ ├── CollectionToStringHelperTest.java │ │ ├── DependencyModelTest.java │ │ ├── functions │ │ │ └── PluginMatcherTest.java │ │ └── ArtifactModelTest.java │ │ ├── ErrorReportAssert.java │ │ ├── util │ │ ├── CommaSeparatorUtilsTest.java │ │ └── EnforcerRuleUtilsTest.java │ │ ├── PedanticDependencyOrderEnforcerTest.java │ │ ├── PedanticDependencyManagementOrderEnforcerTest.java │ │ ├── priority │ │ └── PriorityOrderingTest.java │ │ ├── PedanticModuleOrderEnforcerTest.java │ │ ├── PedanticPluginManagementLocationEnforcerTest.java │ │ ├── PedanticDependencyElementEnforcerTest.java │ │ ├── PedanticPluginElementEnforcerTest.java │ │ ├── PedanticDependencyManagementLocationEnforcerTest.java │ │ ├── PedanticPluginManagementOrderEnforcerTest.java │ │ ├── PedanticPomEnforcersIntegrationTest.java │ │ ├── PedanticPomSectionOrderEnforcerTest.java │ │ ├── AbstractPedanticDependencyOrderEnforcerTest.java │ │ ├── PedanticDependencyScopeEnforcerTest.java │ │ └── AbstractPedanticEnforcerTest.java └── main │ └── java │ └── com │ └── github │ └── ferstl │ └── maven │ └── pomenforcers │ ├── priority │ ├── PriorityOrderingFactory.java │ ├── CompoundPriorityOrdering.java │ └── PriorityOrdering.java │ ├── model │ ├── package-info.java │ ├── DependencyScopeAdapter.java │ ├── functions │ │ ├── StringStartsWithEquivalence.java │ │ ├── StringToArtifactTransformer.java │ │ ├── PluginMatcher.java │ │ ├── AbstractOneToOneMatcher.java │ │ └── DependencyMatcher.java │ ├── CollectionToStringHelper.java │ ├── PluginManagementModel.java │ ├── PluginsModel.java │ ├── DependencyManagementModel.java │ ├── DependencyScope.java │ ├── DependenciesModel.java │ ├── BuildModel.java │ ├── PluginModel.java │ ├── PomSection.java │ ├── PluginElement.java │ ├── DependencyElement.java │ ├── DependencyModel.java │ ├── ProjectModel.java │ └── ArtifactModel.java │ ├── util │ ├── CommaSeparatorUtils.java │ ├── EnforcerRuleUtils.java │ └── XmlUtils.java │ ├── PedanticEnforcerVisitor.java │ ├── PedanticDependencyOrderEnforcer.java │ ├── AbstractPedanticEnforcer.java │ ├── PedanticDependencyManagementOrderEnforcer.java │ ├── ErrorReport.java │ ├── PedanticModuleOrderEnforcer.java │ ├── PedanticPomSectionOrderEnforcer.java │ ├── PedanticPluginManagementLocationEnforcer.java │ ├── PedanticDependencyManagementLocationEnforcer.java │ ├── AbstractPedanticDependencyOrderEnforcer.java │ └── PedanticEnforcerRule.java └── .github └── workflows ├── maven.yml └── codeql-analysis.yml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | target/ 3 | *.iml 4 | .flattened-pom.xml 5 | -------------------------------------------------------------------------------- /src/etc/header.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - 2025 the original author or authors. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /src/test/projects/example-project/module2/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | com.github.ferstl 7 | example-settings-it 8 | 1.0-SNAPSHOT 9 | 10 | 11 | module2 12 | 13 | 14 | 15 | ${project.groupId} 16 | module1 17 | ${project.version} 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/test/projects/example-project/module5/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | com.github.ferstl 7 | example-settings-it 8 | 1.0-SNAPSHOT 9 | 10 | 11 | module5 12 | 13 | 14 | 15 | ${project.groupId} 16 | module1 17 | ${project.version} 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/test/projects/issue-2/jar-module/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | com.github.ferstl 7 | issue-2-it-parent 8 | 1.0-SNAPSHOT 9 | 10 | 11 | issue-2-it-jar 12 | 13 | 14 | 15 | 16 | ${project.groupId} 17 | issue-2-it-pom 18 | ${project.version} 19 | pom 20 | import 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/test/projects/issue-53/module1/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | com.github.ferstl 7 | issue-53-parent 8 | 1.0-SNAPSHOT 9 | 10 | 11 | module1 12 | 13 | 14 | 15 | 16 | org.springframework 17 | spring-jdbc 18 | 19 | 20 | * 21 | * 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/priority/PriorityOrderingFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.priority; 17 | 18 | import java.util.Collection; 19 | 20 | 21 | public interface PriorityOrderingFactory

, T> { 22 | 23 | PriorityOrdering createPriorityOrdering(Collection

priorityCollection); 24 | } 25 | -------------------------------------------------------------------------------- /src/test/projects/issue-2/pom-module/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | com.github.ferstl 7 | issue-2-it-parent 8 | 1.0-SNAPSHOT 9 | 10 | 11 | issue-2-it-pom 12 | pom 13 | 14 | 15 | 16 | 17 | com.googlecode.lambdaj 18 | lambdaj 19 | 2.3.3 20 | 21 | 22 | 23 | 24 | 25 | 26 | com.googlecode.lambdaj 27 | lambdaj 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/test/projects/example-project/module3/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | com.github.ferstl 7 | example-settings-it 8 | 1.0-SNAPSHOT 9 | 10 | 11 | module3 12 | 13 | 14 | 15 | ${project.groupId} 16 | module1 17 | ${project.version} 18 | 19 | 20 | 21 | ${project.groupId} 22 | module1 23 | ${project.version} 24 | tests 25 | test 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @XmlSchema( 17 | namespace = "http://maven.apache.org/POM/4.0.0", 18 | location = "http://maven.apache.org/xsd/maven-4.0.0.xsd") 19 | @XmlAccessorType(FIELD) 20 | package com.github.ferstl.maven.pomenforcers.model; 21 | 22 | import javax.xml.bind.annotation.XmlAccessorType; 23 | import javax.xml.bind.annotation.XmlSchema; 24 | import static javax.xml.bind.annotation.XmlAccessType.FIELD; 25 | 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/DependencyScopeAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import javax.xml.bind.annotation.adapters.XmlAdapter; 19 | 20 | 21 | class DependencyScopeAdapter extends XmlAdapter { 22 | 23 | @Override 24 | public DependencyScope unmarshal(String v) { 25 | return DependencyScope.getByScopeName(v); 26 | } 27 | 28 | @Override 29 | public String marshal(DependencyScope v) { 30 | return v.getScopeName(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/test/projects/plugins/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | plugins 6 | 7 | 8 | 9 | 10 | 11 | org.apache.maven.plugins 12 | maven-jar-plugin 13 | 2.4 14 | false 15 | true 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | org.apache.maven.plugins 25 | maven-jar-plugin 26 | 2.4 27 | false 28 | true 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/model/DependencyElementTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import org.junit.jupiter.api.Test; 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | 22 | class DependencyElementTest { 23 | 24 | @Test 25 | void testGetByElementName() { 26 | DependencyElement.values(); 27 | for (DependencyElement element : DependencyElement.values()) { 28 | assertThat(element).isEqualTo(DependencyElement.getByElementName(element.getElementName())); 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | java: [ '11', '17', '21' ] 16 | steps: 17 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 18 | - name: Set up JDK ${{ matrix.Java }} 19 | uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 20 | with: 21 | java-version: ${{ matrix.Java }} 22 | distribution: 'zulu' 23 | cache: 'maven' 24 | - name: Maven Build 25 | run: mvn clean install javadoc:javadoc 26 | 27 | post-build: 28 | needs: [ build ] 29 | runs-on: ubuntu-latest 30 | steps: 31 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 32 | - name: Set up JDK 33 | uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 34 | with: 35 | java-version: 17 36 | distribution: 'zulu' 37 | cache: maven 38 | - name: Coveralls Report 39 | run: mvn org.jacoco:jacoco-maven-plugin:prepare-agent test org.jacoco:jacoco-maven-plugin:report org.eluder.coveralls:coveralls-maven-plugin:report -DrepoToken=${{ secrets.COVERALLS_TOKEN }} 40 | -------------------------------------------------------------------------------- /src/test/projects/example-project/module4/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | com.github.ferstl 7 | example-settings-it 8 | 1.0-SNAPSHOT 9 | 10 | 11 | module4 12 | 13 | 14 | 15 | ${project.groupId} 16 | module1 17 | ${project.version} 18 | 19 | 20 | ${project.groupId} 21 | module2 22 | ${project.version} 23 | 24 | 25 | ${project.groupId} 26 | module3 27 | ${project.version} 28 | 29 | 30 | 31 | ${project.groupId} 32 | module1 33 | ${project.version} 34 | test-jar 35 | test 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/functions/StringStartsWithEquivalence.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model.functions; 17 | 18 | import com.google.common.base.Equivalence; 19 | 20 | 21 | public class StringStartsWithEquivalence extends Equivalence { 22 | 23 | private static final Equivalence INSTANCE = new StringStartsWithEquivalence(); 24 | 25 | public static Equivalence stringStartsWith() { 26 | return INSTANCE; 27 | } 28 | 29 | @Override 30 | protected boolean doEquivalent(String a, String b) { 31 | return a.startsWith(b); 32 | } 33 | 34 | @Override 35 | protected int doHash(String t) { 36 | return t.hashCode(); 37 | } 38 | 39 | private StringStartsWithEquivalence() { 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/CollectionToStringHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import com.google.common.base.Joiner; 19 | import com.google.common.collect.Iterables; 20 | import static java.util.Collections.emptyList; 21 | 22 | final class CollectionToStringHelper { 23 | 24 | private static final Joiner JOINER = Joiner.on(",\n"); 25 | 26 | public static String toString(String prefix, Iterable iterable) { 27 | Iterable theIterable = iterable != null ? iterable : emptyList(); 28 | 29 | StringBuilder sb = new StringBuilder(prefix).append(" [\n"); 30 | JOINER.appendTo(sb, theIterable).append(!Iterables.isEmpty(theIterable) ? "\n]" : "]"); 31 | return sb.toString(); 32 | } 33 | 34 | private CollectionToStringHelper() { 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/functions/StringToArtifactTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model.functions; 17 | 18 | import java.util.ArrayList; 19 | import com.github.ferstl.maven.pomenforcers.model.ArtifactModel; 20 | import com.google.common.base.Splitter; 21 | import com.google.common.collect.Lists; 22 | 23 | public final class StringToArtifactTransformer { 24 | 25 | private static final Splitter COLON_SPLITTER = Splitter.on(":"); 26 | 27 | public static ArtifactModel toArtifactModel(String input) { 28 | ArrayList artifactElements = Lists.newArrayList(COLON_SPLITTER.split(input)); 29 | 30 | if (artifactElements.size() != 2) { 31 | throw new IllegalArgumentException("Cannot read POM information: " + input); 32 | } 33 | 34 | return new ArtifactModel(artifactElements.get(0), artifactElements.get(1)); 35 | } 36 | 37 | private StringToArtifactTransformer() { 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/model/ModelTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import java.io.File; 19 | import javax.xml.bind.Binder; 20 | import javax.xml.bind.JAXBContext; 21 | import javax.xml.bind.JAXBElement; 22 | import org.junit.jupiter.api.Test; 23 | import org.w3c.dom.Document; 24 | import org.w3c.dom.Node; 25 | import com.github.ferstl.maven.pomenforcers.util.XmlUtils; 26 | import static org.assertj.core.api.Assertions.assertThat; 27 | 28 | class ModelTest { 29 | 30 | @Test 31 | void test() throws Exception { 32 | Document pom = XmlUtils.parseXml(new File("src/test/projects/example-project/pom.xml")); 33 | JAXBContext ctx = JAXBContext.newInstance(ProjectModel.class); 34 | 35 | Binder binder = ctx.createBinder(); 36 | JAXBElement projectModel = binder.unmarshal(pom, ProjectModel.class); 37 | 38 | assertThat(projectModel).isNotNull(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/model/CollectionToStringHelperTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import org.junit.jupiter.api.Test; 19 | import static java.util.Arrays.asList; 20 | import static org.junit.jupiter.api.Assertions.assertEquals; 21 | 22 | class CollectionToStringHelperTest { 23 | 24 | @Test 25 | void toStringWithValues() { 26 | // act 27 | String result = CollectionToStringHelper.toString("Test", asList("a", "b", "c")); 28 | 29 | // assert 30 | String expected = "Test [\n" 31 | + "a,\n" 32 | + "b,\n" 33 | + "c\n" 34 | + "]"; 35 | assertEquals(expected, result); 36 | } 37 | 38 | 39 | @Test 40 | void toStringWithNullCollection() { 41 | // act 42 | String result = CollectionToStringHelper.toString("Test", null); 43 | 44 | // assert 45 | String expected = "Test [\n" 46 | + "]"; 47 | assertEquals(expected, result); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/ErrorReportAssert.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import org.assertj.core.api.AbstractAssert; 19 | 20 | /** 21 | * Matcher that shows the {@link ErrorReport} in case of an unexpected failure. 22 | */ 23 | class ErrorReportAssert extends AbstractAssert { 24 | 25 | public ErrorReportAssert(ErrorReport actual) { 26 | super(actual, ErrorReportAssert.class); 27 | } 28 | 29 | public static ErrorReportAssert assertThat(ErrorReport actual) { 30 | return new ErrorReportAssert(actual); 31 | } 32 | 33 | public ErrorReportAssert hasErrors() { 34 | if (!this.actual.hasErrors()) { 35 | failWithMessage("There were no errors"); 36 | } 37 | 38 | return this; 39 | } 40 | 41 | public ErrorReportAssert hasNoErrors() { 42 | if (this.actual.hasErrors()) { 43 | failWithMessage("There were errors"); 44 | } 45 | 46 | return this; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/model/DependencyModelTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import org.junit.jupiter.api.Test; 19 | import static org.junit.jupiter.api.Assertions.assertEquals; 20 | 21 | 22 | class DependencyModelTest { 23 | 24 | @Test 25 | void toStringWithDefaults() { 26 | DependencyModel model = new DependencyModel("group", "artifact", "1.0.0", null, null, null); 27 | 28 | assertEquals("group:artifact:1.0.0:jar:compile", model.toString()); 29 | } 30 | 31 | @Test 32 | void toStringWithClassifier() { 33 | DependencyModel model = new DependencyModel("group", "artifact", "1.0.0", null, "classifier", null); 34 | 35 | assertEquals("group:artifact:1.0.0:jar:compile:classifier", model.toString()); 36 | } 37 | 38 | @Test 39 | void toStringNoDefaults() { 40 | DependencyModel model = new DependencyModel("group", "artifact", "1.0.0", "test", "classifier", "zip"); 41 | 42 | assertEquals("group:artifact:1.0.0:zip:test:classifier", model.toString()); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/util/CommaSeparatorUtilsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.util; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.Map; 21 | import org.junit.jupiter.api.Test; 22 | import com.google.common.collect.ImmutableList; 23 | import com.google.common.collect.ImmutableMap; 24 | import static org.assertj.core.api.Assertions.assertThat; 25 | 26 | class CommaSeparatorUtilsTest { 27 | 28 | @Test 29 | void testSplitAndAddToCollection() { 30 | Map> tests = ImmutableMap.>builder() 31 | .put("a,b,c", ImmutableList.of("a", "b", "c")) 32 | .put("a,b,,,c,", ImmutableList.of("a", "b", "c")) 33 | .put(" a \n,\n \t b ,\r\n c \t\n", ImmutableList.of("a", "b", "c")) 34 | .build(); 35 | for (Map.Entry> test : tests.entrySet()) { 36 | List l = new ArrayList<>(); 37 | CommaSeparatorUtils.splitAndAddToCollection(test.getKey(), l); 38 | assertThat(l).isEqualTo(test.getValue()); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/PluginManagementModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import java.util.Collections; 19 | import java.util.List; 20 | import java.util.Objects; 21 | import javax.xml.bind.annotation.XmlElement; 22 | 23 | class PluginManagementModel { 24 | 25 | @XmlElement(namespace = "http://maven.apache.org/POM/4.0.0") 26 | private PluginsModel plugins; 27 | 28 | public List getPlugins() { 29 | return this.plugins != null ? this.plugins.getPlugins() : Collections.emptyList(); 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "PluginManagement->" + Objects.toString(this.plugins, "none"); 35 | } 36 | 37 | @Override 38 | public boolean equals(Object obj) { 39 | if (obj == this) { 40 | return true; 41 | } 42 | if (!(obj instanceof PluginManagementModel)) { 43 | return false; 44 | } 45 | PluginManagementModel other = (PluginManagementModel) obj; 46 | return Objects.equals(this.plugins, other.plugins); 47 | } 48 | 49 | @Override 50 | public int hashCode() { 51 | return Objects.hash(this.plugins); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/PluginsModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import java.util.Collections; 19 | import java.util.List; 20 | import java.util.Objects; 21 | import javax.xml.bind.annotation.XmlElement; 22 | 23 | class PluginsModel { 24 | 25 | @XmlElement(name = "plugin", namespace = "http://maven.apache.org/POM/4.0.0") 26 | private List plugins; 27 | 28 | public PluginsModel() { 29 | } 30 | 31 | public List getPlugins() { 32 | return this.plugins != null ? this.plugins : Collections.emptyList(); 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return CollectionToStringHelper.toString("Plugins", this.plugins); 38 | } 39 | 40 | @Override 41 | public boolean equals(Object obj) { 42 | if (obj == this) { 43 | return true; 44 | } 45 | if (!(obj instanceof PluginsModel)) { 46 | return false; 47 | } 48 | PluginsModel other = (PluginsModel) obj; 49 | return getPlugins().equals(other.getPlugins()); 50 | } 51 | 52 | @Override 53 | public int hashCode() { 54 | return Objects.hash(getPlugins()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/util/CommaSeparatorUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.util; 17 | 18 | import java.util.Collection; 19 | import java.util.function.Function; 20 | import java.util.stream.StreamSupport; 21 | import com.google.common.base.Splitter; 22 | import static java.util.stream.Collectors.toCollection; 23 | 24 | public final class CommaSeparatorUtils { 25 | 26 | private static final Splitter COMMA_SPLITTER = Splitter.on(",").trimResults().omitEmptyStrings(); 27 | 28 | public static void splitAndAddToCollection(String commaSeparatedItems, Collection collection) { 29 | splitAndAddToCollection(commaSeparatedItems, collection, Function.identity()); 30 | } 31 | 32 | public static void splitAndAddToCollection(String commaSeparatedItems, Collection collection, Function transformer) { 33 | Iterable items = COMMA_SPLITTER.split(commaSeparatedItems); 34 | // Don't touch the collection if there is nothing to add. 35 | if (items.iterator().hasNext()) { 36 | collection.clear(); 37 | } 38 | StreamSupport.stream(items.spliterator(), false) 39 | .map(transformer) 40 | .collect(toCollection(() -> collection)); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/PedanticDependencyOrderEnforcerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import org.junit.jupiter.api.Test; 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | import static org.mockito.Mockito.mock; 21 | import static org.mockito.Mockito.verify; 22 | 23 | /** 24 | * JUnit tests for {@link PedanticDependencyOrderEnforcer}. 25 | */ 26 | class PedanticDependencyOrderEnforcerTest extends AbstractPedanticDependencyOrderEnforcerTest { 27 | 28 | @Override 29 | PedanticDependencyOrderEnforcer createRule() { 30 | return new PedanticDependencyOrderEnforcer(this.mockMavenProject, this.mockHelper); 31 | } 32 | 33 | @Override 34 | @Test 35 | void getDescription() { 36 | assertThat(this.testRule.getDescription()).isEqualTo(PedanticEnforcerRule.DEPENDENCY_ORDER); 37 | } 38 | 39 | @Override 40 | @Test 41 | void accept() { 42 | PedanticEnforcerVisitor visitor = mock(PedanticEnforcerVisitor.class); 43 | this.testRule.accept(visitor); 44 | 45 | verify(visitor).visit(this.testRule); 46 | } 47 | 48 | @Override 49 | protected DependencyAdder createDependencyAdder() { 50 | return this::addDependency; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/DependencyManagementModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import java.util.Collections; 19 | import java.util.List; 20 | import java.util.Objects; 21 | import javax.xml.bind.annotation.XmlElement; 22 | 23 | 24 | class DependencyManagementModel { 25 | 26 | @XmlElement(namespace = "http://maven.apache.org/POM/4.0.0") 27 | private DependenciesModel dependencies; 28 | 29 | public List getDependencies() { 30 | return this.dependencies != null ? this.dependencies.getDependencies() : Collections.emptyList(); 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | return "DependencyManagement->" + Objects.toString(this.dependencies, "none"); 36 | } 37 | 38 | @Override 39 | public boolean equals(Object obj) { 40 | if (obj == this) { 41 | return true; 42 | } 43 | if (!(obj instanceof DependencyManagementModel)) { 44 | return false; 45 | } 46 | DependencyManagementModel other = (DependencyManagementModel) obj; 47 | return Objects.equals(this.dependencies, other.dependencies); 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | return this.dependencies != null ? this.dependencies.hashCode() : 0; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/DependencyScope.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import java.util.LinkedHashMap; 19 | import java.util.Map; 20 | import static java.util.Objects.requireNonNull; 21 | 22 | public enum DependencyScope { 23 | 24 | IMPORT("import"), 25 | COMPILE("compile"), 26 | PROVIDED("provided"), 27 | RUNTIME("runtime"), 28 | SYSTEM("system"), 29 | TEST("test"); 30 | 31 | private static final Map dependencyScopeMap; 32 | 33 | static { 34 | dependencyScopeMap = new LinkedHashMap<>(); 35 | for (DependencyScope scope : values()) { 36 | dependencyScopeMap.put(scope.getScopeName(), scope); 37 | } 38 | } 39 | 40 | public static DependencyScope getByScopeName(String scopeName) { 41 | requireNonNull(scopeName, "Scope name is null."); 42 | 43 | DependencyScope scope = dependencyScopeMap.get(scopeName); 44 | if (scope == null) { 45 | throw new IllegalArgumentException("Dependency scope'" + scopeName + "' does not exist."); 46 | } 47 | 48 | return scope; 49 | } 50 | 51 | private final String scopeName; 52 | 53 | DependencyScope(String name) { 54 | this.scopeName = name; 55 | } 56 | 57 | public String getScopeName() { 58 | return this.scopeName; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/PedanticDependencyManagementOrderEnforcerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import org.junit.jupiter.api.Test; 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | import static org.mockito.Mockito.mock; 21 | import static org.mockito.Mockito.verify; 22 | 23 | /** 24 | * JUnit tests for {@link PedanticDependencyManagementOrderEnforcer}: 25 | */ 26 | class PedanticDependencyManagementOrderEnforcerTest extends AbstractPedanticDependencyOrderEnforcerTest { 27 | 28 | @Override 29 | PedanticDependencyManagementOrderEnforcer createRule() { 30 | return new PedanticDependencyManagementOrderEnforcer(this.mockMavenProject, this.mockHelper); 31 | } 32 | 33 | @Test 34 | @Override 35 | void getDescription() { 36 | assertThat(this.testRule.getDescription()).isEqualTo(PedanticEnforcerRule.DEPENDENCY_MANAGEMENT_ORDER); 37 | } 38 | 39 | @Test 40 | @Override 41 | void accept() { 42 | PedanticEnforcerVisitor visitor = mock(PedanticEnforcerVisitor.class); 43 | this.testRule.accept(visitor); 44 | 45 | verify(visitor).visit(this.testRule); 46 | } 47 | 48 | @Override 49 | protected DependencyAdder createDependencyAdder() { 50 | return this::addManagedDependency; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/PedanticEnforcerVisitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | public interface PedanticEnforcerVisitor { 19 | 20 | void visit(PedanticPomSectionOrderEnforcer sectionOrderEnforcer); 21 | 22 | void visit(PedanticModuleOrderEnforcer moduleOrderEnforcer); 23 | 24 | void visit(PedanticDependencyManagementOrderEnforcer dependencyManagementOrderEnforcer); 25 | 26 | void visit(PedanticDependencyManagementLocationEnforcer pedanticDependencyManagementLocationEnforcer); 27 | 28 | void visit(PedanticDependencyOrderEnforcer dependencyOrderEnforcer); 29 | 30 | void visit(PedanticDependencyConfigurationEnforcer pedanticDependencyConfigurationEnforcer); 31 | 32 | void visit(PedanticDependencyScopeEnforcer pedanticDependencyScopeEnforcer); 33 | 34 | void visit(PedanticPluginManagementOrderEnforcer pluginManagementOrderEnforcer); 35 | 36 | void visit(CompoundPedanticEnforcer compoundEnforcer); 37 | 38 | void visit(PedanticPluginConfigurationEnforcer pedanticPluginConfigurationEnforcer); 39 | 40 | void visit(PedanticPluginManagementLocationEnforcer pedanticPluginManagementLocationEnforcer); 41 | 42 | void visit(PedanticDependencyElementEnforcer pedanticDependencyElementEnforcer); 43 | 44 | void visit(PedanticPluginElementEnforcer pedanticPluginElementEnforcer); 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/util/EnforcerRuleUtilsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.util; 17 | 18 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 19 | import org.junit.jupiter.api.BeforeEach; 20 | import org.junit.jupiter.api.Test; 21 | import static com.github.ferstl.maven.pomenforcers.util.EnforcerRuleUtils.evaluateProperties; 22 | import static org.assertj.core.api.Assertions.assertThat; 23 | import static org.mockito.ArgumentMatchers.anyString; 24 | import static org.mockito.Mockito.mock; 25 | import static org.mockito.Mockito.when; 26 | 27 | class EnforcerRuleUtilsTest { 28 | 29 | private ExpressionEvaluator mockHelper; 30 | 31 | @BeforeEach 32 | void setup() throws Exception { 33 | this.mockHelper = mock(ExpressionEvaluator.class); 34 | when(this.mockHelper.evaluate(anyString())).thenReturn("test"); 35 | } 36 | 37 | @Test 38 | void testEvaluateProperties() { 39 | assertThat(evaluateProperties("foo-${user.name}-bar", this.mockHelper)).isEqualTo("foo-test-bar"); 40 | assertThat(evaluateProperties("foo-${x}-bar-${y}", this.mockHelper)).isEqualTo("foo-test-bar-test"); 41 | assertThat(evaluateProperties("foo", this.mockHelper)).isEqualTo("foo"); 42 | assertThat(evaluateProperties("", this.mockHelper)).isEqualTo(""); 43 | assertThat(evaluateProperties(null, this.mockHelper)).isNull(); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/test/projects/issue-2/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.github.ferstl 6 | issue-2-it-parent 7 | 1.0-SNAPSHOT 8 | pom 9 | 10 | 11 | Integration test for issue #2. See https://github.com/ferstl/pedantic-pom-enforcers/pull/2 . 12 | This project contains two modules. pom-module has <packaging>pom</packaging>. 13 | jar-module has <packaging>jar</packaging> and an import-scope dependency to pom-module. 14 | 15 | 16 | 17 | jar-module 18 | pom-module 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.apache.maven.plugins 26 | maven-enforcer-plugin 27 | 3.6.0 28 | 29 | 30 | 31 | 32 | DEPENDENCY_MANAGEMENT_ORDER,DEPENDENCY_ORDER 33 | 34 | 35 | true 36 | 37 | 38 | 39 | com.github.ferstl 40 | pedantic-pom-enforcers 41 | ${it-plugin.version} 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.apache.maven.plugins 50 | maven-enforcer-plugin 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/test/projects/issue-53/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.github.ferstl 6 | issue-53-parent 7 | 1.0-SNAPSHOT 8 | pom 9 | 10 | 11 | Investigatin of https://github.com/ferstl/pedantic-pom-enforcers/issues/53 12 | 13 | 14 | 15 | module1 16 | 17 | 18 | 19 | 20 | 21 | org.springframework 22 | spring-jdbc 23 | 6.2.12 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-enforcer-plugin 34 | 3.6.0 35 | 36 | 37 | 38 | 39 | 40 | DEPENDENCY_CONFIGURATION 41 | 42 | 43 | 44 | true 45 | 46 | 47 | 48 | com.github.ferstl 49 | pedantic-pom-enforcers 50 | ${it-plugin.version} 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.apache.maven.plugins 59 | maven-enforcer-plugin 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/DependenciesModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collection; 20 | import java.util.Collections; 21 | import java.util.List; 22 | import java.util.Objects; 23 | import javax.xml.bind.annotation.XmlElement; 24 | 25 | class DependenciesModel { 26 | 27 | @XmlElement(name = "dependency", namespace = "http://maven.apache.org/POM/4.0.0") 28 | private List dependencies; 29 | 30 | // Constructor used by JAXB 31 | DependenciesModel() { 32 | } 33 | 34 | public DependenciesModel(Collection dependencies) { 35 | this.dependencies = new ArrayList<>(); 36 | this.dependencies.addAll(dependencies); 37 | } 38 | 39 | 40 | public List getDependencies() { 41 | return this.dependencies != null ? this.dependencies : Collections.emptyList(); 42 | } 43 | 44 | @Override 45 | public boolean equals(Object obj) { 46 | if (obj == this) { 47 | return true; 48 | } 49 | if (!(obj instanceof DependenciesModel)) { 50 | return false; 51 | } 52 | 53 | DependenciesModel other = (DependenciesModel) obj; 54 | return getDependencies().equals(other.getDependencies()); 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | return Objects.hash(getDependencies()); 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return CollectionToStringHelper.toString("Dependencies", this.dependencies); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/test/projects/example-project/module1/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | com.github.ferstl 7 | example-settings-it 8 | 1.0-SNAPSHOT 9 | 10 | 11 | module1 12 | 13 | 14 | commons-,org.hamcrest 15 | 16 | 17 | 18 | 19 | commons-lang 20 | commons-lang 21 | 22 | 23 | com.googlecode.lambdaj 24 | lambdaj 25 | 26 | 27 | javax.servlet 28 | servlet-api 29 | 30 | 31 | junit 32 | junit 33 | 34 | 35 | org.hamcrest 36 | hamcrest-library 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | org.apache.maven.plugins 45 | maven-jar-plugin 46 | 2.4 47 | 48 | 49 | org.apache.maven.plugins 50 | maven-release-plugin 51 | 2.3 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.apache.maven.plugins 59 | maven-jar-plugin 60 | 61 | 62 | 63 | test-jar 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/BuildModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import java.util.Collections; 19 | import java.util.List; 20 | import java.util.Objects; 21 | import javax.xml.bind.annotation.XmlElement; 22 | import com.google.common.base.Joiner; 23 | 24 | class BuildModel { 25 | 26 | private static final Joiner TO_STRING_JOINER = Joiner.on("\n"); 27 | 28 | @XmlElement(namespace = "http://maven.apache.org/POM/4.0.0") 29 | private PluginManagementModel pluginManagement; 30 | 31 | @XmlElement(namespace = "http://maven.apache.org/POM/4.0.0") 32 | private PluginsModel plugins; 33 | 34 | 35 | public List getManagedPlugins() { 36 | return this.pluginManagement != null ? this.pluginManagement.getPlugins() : Collections.emptyList(); 37 | } 38 | 39 | public List getPlugins() { 40 | return this.plugins != null ? this.plugins.getPlugins() : Collections.emptyList(); 41 | } 42 | 43 | @Override 44 | public boolean equals(Object obj) { 45 | if (obj == this) { 46 | return true; 47 | } 48 | if (!(obj instanceof BuildModel)) { 49 | return false; 50 | } 51 | 52 | BuildModel other = (BuildModel) obj; 53 | return Objects.equals(this.pluginManagement, other.pluginManagement) 54 | && Objects.equals(this.plugins, other.plugins); 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | return Objects.hash(this.pluginManagement, this.plugins); 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return TO_STRING_JOINER.join(this.pluginManagement, this.plugins); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/util/EnforcerRuleUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.util; 17 | 18 | import java.util.regex.Matcher; 19 | import java.util.regex.Pattern; 20 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; 21 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 22 | import com.google.common.base.Strings; 23 | 24 | public final class EnforcerRuleUtils { 25 | 26 | private static final Pattern PROPERTY_PATTERN = Pattern.compile("\\$\\{.*?}"); 27 | 28 | public static String evaluateProperties(String input, ExpressionEvaluator helper) { 29 | if (!Strings.isNullOrEmpty(input)) { 30 | Matcher matcher = PROPERTY_PATTERN.matcher(input); 31 | StringBuffer substituted = new StringBuffer(); 32 | while (matcher.find()) { 33 | String property = matcher.group(); 34 | matcher.appendReplacement(substituted, evaluateStringProperty(property, helper)); 35 | } 36 | matcher.appendTail(substituted); 37 | return substituted.toString(); 38 | } 39 | return input; 40 | } 41 | 42 | private static String evaluateStringProperty(String property, ExpressionEvaluator helper) { 43 | try { 44 | return (String) helper.evaluate(property); 45 | } catch (ExpressionEvaluationException e) { 46 | throw new IllegalArgumentException("Unable to resolve property " + property); 47 | } catch (ClassCastException e) { 48 | throw new IllegalArgumentException("Property " + property + " does not evaluate to a String"); 49 | } 50 | } 51 | 52 | private EnforcerRuleUtils() { 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/functions/PluginMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model.functions; 17 | 18 | import java.util.Objects; 19 | import org.apache.maven.model.Plugin; 20 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 21 | import com.github.ferstl.maven.pomenforcers.model.PluginModel; 22 | import static com.github.ferstl.maven.pomenforcers.util.EnforcerRuleUtils.evaluateProperties; 23 | import static com.google.common.base.Strings.isNullOrEmpty; 24 | 25 | /** 26 | * Matches Maven {@link Plugin} objects with {@link PluginModel} objects. 27 | */ 28 | public class PluginMatcher extends AbstractOneToOneMatcher { 29 | 30 | private static final String DEFAULT_GROUP_ID = "org.apache.maven.plugins"; 31 | 32 | public PluginMatcher(ExpressionEvaluator helper) { 33 | super(helper); 34 | } 35 | 36 | @Override 37 | protected PluginModel transform(Plugin mavenPlugin) { 38 | return new PluginModel(mavenPlugin.getGroupId(), mavenPlugin.getArtifactId(), mavenPlugin.getVersion()); 39 | } 40 | 41 | @Override 42 | protected boolean matches(PluginModel supersetItem, PluginModel subsetItem) { 43 | String groupId = getGroupId(subsetItem); 44 | String artifactId = evaluateProperties(subsetItem.getArtifactId(), getHelper()); 45 | 46 | return Objects.equals(supersetItem.getGroupId(), groupId) 47 | && Objects.equals(supersetItem.getArtifactId(), artifactId); 48 | } 49 | 50 | private String getGroupId(PluginModel plugin) { 51 | String groupId = evaluateProperties(plugin.getGroupId(), getHelper()); 52 | return !isNullOrEmpty(groupId) ? groupId : DEFAULT_GROUP_ID; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/test/projects/warn-only/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | Integration test that test the warnOnly flag. Note that this section is at the wrong place according to the 7 | Pedantic POM Section Order Enforcer. 8 | 9 | 10 | com.github.ferstl 11 | warn-only 12 | 1.0-SNAPSHOT 13 | 14 | 15 | 16 | 17 | junit 18 | junit 19 | 4.13.2 20 | test 21 | 22 | 23 | org.apache.commons 24 | commons-lang3 25 | 3.3.2 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | org.apache.maven.plugins 34 | maven-enforcer-plugin 35 | 3.6.0 36 | 37 | 38 | 39 | true 40 | 41 | 42 | true 43 | 44 | 45 | 46 | 47 | 48 | com.github.ferstl 49 | pedantic-pom-enforcers 50 | ${it-plugin.version} 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.apache.maven.plugins 59 | maven-enforcer-plugin 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/test/projects/simple-project/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.github.ferstl 6 | simple-project-it 7 | 1.0-SNAPSHOT 8 | 9 | 10 | Integration test which verifies that the enforcer rules work with the simplest possible maven project. 11 | 12 | 13 | 14 | 15 | 16 | 17 | org.apache.maven.plugins 18 | maven-enforcer-plugin 19 | 3.6.0 20 | 21 | 22 | 23 | 24 | 25 | POM_SECTION_ORDER,MODULE_ORDER,DEPENDENCY_MANAGEMENT_ORDER,DEPENDENCY_ORDER,DEPENDENCY_SCOPE,DEPENDENCY_CONFIGURATION,DEPENDENCY_MANAGEMENT_LOCATION,PLUGIN_MANAGEMENT_ORDER,PLUGIN_CONFIGURATION,PLUGIN_MANAGEMENT_LOCATION 26 | 27 | 28 | 30 | com.github.ferstl:simple-project-it 31 | com.github.ferstl:simple-project-it 32 | 33 | 34 | true 35 | 36 | 37 | 38 | com.github.ferstl 39 | pedantic-pom-enforcers 40 | ${it-plugin.version} 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | org.apache.maven.plugins 49 | maven-enforcer-plugin 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/test/projects/issue-23/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.github.ferstl 6 | issue-23 7 | 1.0-SNAPSHOT 8 | 9 | 10 | Integration test which verifies that the enforcer rules work with the simplest possible maven project. 11 | 12 | 13 | 14 | 15 | 16 | org.springframework 17 | spring-jdbc 18 | 5.1.3.RELEASE 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.springframework 26 | spring-jdbc 27 | 28 | 29 | * 30 | * 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-enforcer-plugin 42 | 3.6.0 43 | 44 | 45 | 46 | 47 | 48 | DEPENDENCY_CONFIGURATION 49 | 50 | 51 | 52 | true 53 | 54 | 55 | 56 | com.github.ferstl 57 | pedantic-pom-enforcers 58 | ${it-plugin.version} 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-enforcer-plugin 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/PluginModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import java.util.Collections; 19 | import java.util.List; 20 | import java.util.Objects; 21 | import javax.xml.bind.annotation.XmlAnyElement; 22 | import javax.xml.bind.annotation.XmlElement; 23 | import javax.xml.bind.annotation.XmlElementWrapper; 24 | import org.w3c.dom.Element; 25 | 26 | public class PluginModel extends ArtifactModel { 27 | 28 | @XmlElementWrapper(name = "configuration", namespace = "http://maven.apache.org/POM/4.0.0") 29 | @XmlAnyElement 30 | private List configItems; 31 | 32 | @XmlElement(namespace = "http://maven.apache.org/POM/4.0.0") 33 | private DependenciesModel dependencies; 34 | 35 | PluginModel() { 36 | } 37 | 38 | public PluginModel(String groupId, String artifactId, String version) { 39 | super(groupId, artifactId, version); 40 | } 41 | 42 | public boolean isConfigured() { 43 | return this.configItems != null && !this.configItems.isEmpty(); 44 | } 45 | 46 | public List getDependencies() { 47 | return this.dependencies != null ? this.dependencies.getDependencies() : Collections.emptyList(); 48 | } 49 | 50 | @Override 51 | public boolean equals(Object obj) { 52 | if (obj == this) { 53 | return true; 54 | } 55 | if (!(obj instanceof PluginModel)) { 56 | return false; 57 | } 58 | 59 | PluginModel other = (PluginModel) obj; 60 | return super.equals(other) 61 | // TODO: Element implementations may not implement equals()!! 62 | && Objects.equals(this.configItems, other.configItems) 63 | && Objects.equals(this.dependencies, other.dependencies); 64 | } 65 | 66 | @Override 67 | public int hashCode() { 68 | return Objects.hash(super.hashCode(), this.configItems, this.dependencies); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/priority/PriorityOrderingTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.priority; 17 | 18 | import java.util.ArrayList; 19 | import java.util.function.Function; 20 | import org.junit.jupiter.api.Test; 21 | import com.google.common.collect.Lists; 22 | import static org.assertj.core.api.Assertions.assertThat; 23 | 24 | 25 | class PriorityOrderingTest { 26 | 27 | 28 | @Test 29 | void testCompare() { 30 | ArrayList prioritizedItems = Lists.newArrayList("z", "y", "x"); 31 | Function transformer = Function.identity(); 32 | PriorityOrdering testComparator = new PriorityOrdering<>(prioritizedItems, transformer); 33 | 34 | // x is in the priority list, a isn't -> a > x 35 | assertThat(testComparator.compare("a", "x")).isGreaterThan(0); 36 | // x is located after y in the priority list -> x > y 37 | assertThat(testComparator.compare("x", "y")).isGreaterThan(0); 38 | // x is located after y in the priority list -> y < x 39 | assertThat(testComparator.compare("y", "x")).isLessThan(0); 40 | // equality applies to values in the priority list 41 | assertThat(testComparator.compare("x", "x")).isEqualTo(0); 42 | // regular comparison for values that are not in the priority list 43 | assertThat(testComparator.compare("b", "c")).isLessThan(0); 44 | assertThat(testComparator.compare("b", "b")).isEqualTo(0); 45 | assertThat(testComparator.compare("c", "b")).isGreaterThan(0); 46 | } 47 | 48 | @Test 49 | void testCompareWithoutPriorities() { 50 | ArrayList prioritizedItems = Lists.newArrayList(); 51 | Function identity = Function.identity(); 52 | PriorityOrdering testComparator = new PriorityOrdering<>(prioritizedItems, identity); 53 | 54 | assertThat(testComparator.compare("a", "b")).isLessThan(0); 55 | assertThat(testComparator.compare("a", "a")).isEqualTo(0); 56 | assertThat(testComparator.compare("b", "a")).isGreaterThan(0); 57 | } 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | workflow_dispatch: 21 | schedule: 22 | - cron: '38 17 * * 6' 23 | 24 | jobs: 25 | analyze: 26 | name: Analyze 27 | runs-on: ubuntu-latest 28 | permissions: 29 | actions: read 30 | contents: read 31 | security-events: write 32 | 33 | strategy: 34 | fail-fast: false 35 | matrix: 36 | language: [ 'java' ] 37 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 38 | # Learn more: 39 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 40 | 41 | steps: 42 | - name: Checkout repository 43 | uses: actions/checkout@v2 44 | 45 | # Initializes the CodeQL tools for scanning. 46 | - name: Initialize CodeQL 47 | uses: github/codeql-action/init@v1 48 | with: 49 | languages: ${{ matrix.language }} 50 | # If you wish to specify custom queries, you can do so here or in a config file. 51 | # By default, queries listed here will override any specified in a config file. 52 | # Prefix the list here with "+" to use these queries and those in the config file. 53 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 54 | 55 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 56 | # If this step fails, then you should remove it and run the build manually (see below) 57 | - name: Autobuild 58 | uses: github/codeql-action/autobuild@v1 59 | 60 | # ℹ️ Command-line programs to run using the OS shell. 61 | # 📚 https://git.io/JvXDl 62 | 63 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 64 | # and modify them (or add more) to build your code if your project 65 | # uses a compiled language 66 | 67 | #- run: | 68 | # make bootstrap 69 | # make release 70 | 71 | - name: Perform CodeQL Analysis 72 | uses: github/codeql-action/analyze@v1 73 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/PomSection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import java.util.Map; 19 | import com.google.common.collect.Maps; 20 | import static java.util.Objects.requireNonNull; 21 | 22 | 23 | public enum PomSection { 24 | MODEL_VERSION("modelVersion"), 25 | PREREQUISITES("prerequisites"), 26 | PARENT("parent"), 27 | GROUP_ID("groupId"), 28 | ARTIFACT_ID("artifactId"), 29 | VERSION("version"), 30 | PACKAGING("packaging"), 31 | NAME("name"), 32 | DESCRIPTION("description"), 33 | URL("url"), 34 | LICENSES("licenses"), 35 | ORGANIZATION("organization"), 36 | INCEPTION_YEAR("inceptionYear"), 37 | CI_MANAGEMENT("ciManagement"), 38 | MAILING_LISTS("mailingLists"), 39 | ISSUE_MANAGEMENT("issueManagement"), 40 | DEVELOPERS("developers"), 41 | CONTRIBUTORS("contributors"), 42 | SCM("scm"), 43 | REPOSITORIES("repositories"), 44 | PLUGIN_REPOSITORIES("pluginRepositories"), 45 | DISTRIBUTION_MANAGEMENT("distributionManagement"), 46 | MODULES("modules"), 47 | PROPERTIES("properties"), 48 | DEPENDENCY_MANAGEMENT("dependencyManagement"), 49 | DEPENDENCIES("dependencies"), 50 | BUILD("build"), 51 | PROFILES("profiles"), 52 | REPORTING("reporting"), 53 | REPORTS("reports"); 54 | 55 | private static final Map pomSectionMap; 56 | 57 | static { 58 | pomSectionMap = Maps.newHashMap(); 59 | for (PomSection pomSection : values()) { 60 | pomSectionMap.put(pomSection.getSectionName(), pomSection); 61 | } 62 | } 63 | 64 | public static PomSection getBySectionName(String sectionName) { 65 | requireNonNull(sectionName, "Section name is null."); 66 | 67 | PomSection value = pomSectionMap.get(sectionName); 68 | if (value == null) { 69 | throw new IllegalArgumentException("POM section " + sectionName + " does not exist."); 70 | } 71 | 72 | return value; 73 | } 74 | 75 | private final String sectionName; 76 | 77 | PomSection(String sectionName) { 78 | this.sectionName = sectionName; 79 | } 80 | 81 | public String getSectionName() { 82 | return this.sectionName; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/functions/AbstractOneToOneMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model.functions; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collection; 20 | import java.util.List; 21 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 22 | import com.google.common.collect.BiMap; 23 | import com.google.common.collect.ImmutableBiMap; 24 | import com.google.common.collect.ImmutableBiMap.Builder; 25 | 26 | 27 | public abstract class AbstractOneToOneMatcher { 28 | 29 | private final ExpressionEvaluator helper; 30 | 31 | AbstractOneToOneMatcher(ExpressionEvaluator helper) { 32 | this.helper = helper; 33 | } 34 | 35 | public final BiMap match(Collection superset, Collection subset) { 36 | Builder mapBuilder = ImmutableBiMap.builder(); 37 | 38 | // Transform the superset here in order not to do it in each nested loop 39 | Collection transformedSuperset = transformSuperset(superset); 40 | 41 | for (V subsetItem : subset) { 42 | boolean itemMatched = false; 43 | 44 | for (V supersetItem : transformedSuperset) { 45 | if (matches(supersetItem, subsetItem)) { 46 | itemMatched = true; 47 | mapBuilder.put(supersetItem, subsetItem); 48 | break; 49 | } 50 | } 51 | 52 | if (!itemMatched) { 53 | handleUnmatchedItem(mapBuilder, subsetItem); 54 | } 55 | } 56 | 57 | return mapBuilder.build(); 58 | } 59 | 60 | protected void handleUnmatchedItem(Builder mapBuilder, V subsetItem) { 61 | throw new IllegalArgumentException("Could not match item " + subsetItem + " with superset"); 62 | } 63 | 64 | protected abstract V transform(U supersetItem); 65 | 66 | protected abstract boolean matches(V supersetItem, V subsetItem); 67 | 68 | ExpressionEvaluator getHelper() { 69 | return this.helper; 70 | } 71 | 72 | private Collection transformSuperset(Collection superset) { 73 | List transformed = new ArrayList<>(superset.size()); 74 | for (U supersetItem : superset) { 75 | transformed.add(transform(supersetItem)); 76 | } 77 | return transformed; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/model/functions/PluginMatcherTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model.functions; 17 | 18 | import org.apache.maven.model.Plugin; 19 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 20 | import org.junit.jupiter.api.BeforeEach; 21 | import org.junit.jupiter.api.Test; 22 | import com.github.ferstl.maven.pomenforcers.model.PluginModel; 23 | import static org.junit.jupiter.api.Assertions.assertEquals; 24 | import static org.junit.jupiter.api.Assertions.assertTrue; 25 | import static org.mockito.Mockito.mock; 26 | 27 | public class PluginMatcherTest { 28 | 29 | private PluginMatcher pluginMatcher; 30 | 31 | @BeforeEach 32 | public void before() { 33 | this.pluginMatcher = new PluginMatcher(mock(ExpressionEvaluator.class)); 34 | } 35 | 36 | @Test 37 | public void transform() { 38 | // arrange 39 | Plugin plugin = new Plugin(); 40 | plugin.setGroupId("a"); 41 | plugin.setArtifactId("b"); 42 | plugin.setVersion("c"); 43 | 44 | // act 45 | PluginModel pluginModel = this.pluginMatcher.transform(plugin); 46 | 47 | // assert 48 | assertEquals("a", pluginModel.getGroupId()); 49 | assertEquals("b", pluginModel.getArtifactId()); 50 | assertEquals("c", pluginModel.getVersion()); 51 | } 52 | 53 | @Test 54 | public void matchWithAllGavParameters() { 55 | PluginModel supersetPlugin = new PluginModel("a", "b", "c"); 56 | PluginModel subsetPlugin = new PluginModel("a", "b", "c"); 57 | 58 | assertTrue(this.pluginMatcher.matches(supersetPlugin, subsetPlugin)); 59 | } 60 | 61 | @Test 62 | public void matchWithDefaultGroupIdForNull() { 63 | PluginModel supersetPlugin = new PluginModel("org.apache.maven.plugins", "b", "c"); 64 | PluginModel subsetPlugin = new PluginModel(null, "b", "c"); 65 | 66 | assertTrue(this.pluginMatcher.matches(supersetPlugin, subsetPlugin)); 67 | } 68 | 69 | @Test 70 | public void matchWithDefaultGroupIdForEmpty() { 71 | PluginModel supersetPlugin = new PluginModel("org.apache.maven.plugins", "b", "c"); 72 | PluginModel subsetPlugin = new PluginModel("", "b", "c"); 73 | 74 | assertTrue(this.pluginMatcher.matches(supersetPlugin, subsetPlugin)); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/PluginElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import java.util.Collection; 19 | import java.util.Map; 20 | import java.util.function.Function; 21 | import com.github.ferstl.maven.pomenforcers.priority.PriorityOrdering; 22 | import com.github.ferstl.maven.pomenforcers.priority.PriorityOrderingFactory; 23 | import com.google.common.collect.Maps; 24 | import static com.github.ferstl.maven.pomenforcers.model.functions.StringStartsWithEquivalence.stringStartsWith; 25 | import static java.util.Objects.requireNonNull; 26 | 27 | public enum PluginElement implements PriorityOrderingFactory, Function { 28 | 29 | GROUP_ID("groupId") { 30 | @Override 31 | public PriorityOrdering createPriorityOrdering(Collection priorityCollection) { 32 | return new PriorityOrdering<>(priorityCollection, this, stringStartsWith()); 33 | } 34 | 35 | @Override 36 | public String apply(PluginModel input) { 37 | return input.getGroupId(); 38 | } 39 | }, 40 | 41 | ARTIFACT_ID("artifactId") { 42 | @Override 43 | public PriorityOrdering createPriorityOrdering(Collection priorityCollection) { 44 | return new PriorityOrdering<>(priorityCollection, this, stringStartsWith()); 45 | } 46 | 47 | @Override 48 | public String apply(PluginModel input) { 49 | return input.getArtifactId(); 50 | } 51 | }; 52 | 53 | private static final Map elementMap; 54 | 55 | static { 56 | elementMap = Maps.newLinkedHashMap(); 57 | for (PluginElement element : values()) { 58 | elementMap.put(element.getElementName(), element); 59 | } 60 | } 61 | 62 | private final String elementName; 63 | 64 | PluginElement(String elementName) { 65 | this.elementName = elementName; 66 | } 67 | 68 | public String getElementName() { 69 | return this.elementName; 70 | } 71 | 72 | public static PluginElement getByElementName(String elementName) { 73 | requireNonNull(elementName, "Element name is null"); 74 | 75 | PluginElement result = elementMap.get(elementName); 76 | if (result == null) { 77 | throw new IllegalArgumentException("No plugin element with name " + elementName); 78 | } 79 | 80 | return result; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/functions/DependencyMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model.functions; 17 | 18 | import java.util.Objects; 19 | import org.apache.maven.model.Dependency; 20 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 21 | import com.github.ferstl.maven.pomenforcers.model.DependencyModel; 22 | import com.github.ferstl.maven.pomenforcers.model.DependencyScope; 23 | import com.google.common.collect.ImmutableBiMap.Builder; 24 | import static com.github.ferstl.maven.pomenforcers.util.EnforcerRuleUtils.evaluateProperties; 25 | 26 | /** 27 | * Matches Maven {@link Dependency} objects with {@link DependencyModel} objects. 28 | */ 29 | public class DependencyMatcher extends AbstractOneToOneMatcher { 30 | 31 | public DependencyMatcher(ExpressionEvaluator helper) { 32 | super(helper); 33 | } 34 | 35 | @Override 36 | protected DependencyModel transform(Dependency mavenDependency) { 37 | return new DependencyModel( 38 | mavenDependency.getGroupId(), 39 | mavenDependency.getArtifactId(), 40 | mavenDependency.getVersion(), 41 | mavenDependency.getScope(), 42 | mavenDependency.getClassifier(), 43 | mavenDependency.getType()); 44 | } 45 | 46 | @Override 47 | protected boolean matches(DependencyModel supersetItem, DependencyModel subsetItem) { 48 | String groupId = evaluateProperties(subsetItem.getGroupId(), getHelper()); 49 | String artifactId = evaluateProperties(subsetItem.getArtifactId(), getHelper()); 50 | String classifier = evaluateProperties(subsetItem.getClassifier(), getHelper()); 51 | String type = evaluateProperties(subsetItem.getType(), getHelper()); 52 | 53 | return Objects.equals(supersetItem.getGroupId(), groupId) 54 | && Objects.equals(supersetItem.getArtifactId(), artifactId) 55 | && Objects.equals(supersetItem.getClassifier(), classifier) 56 | && Objects.equals(supersetItem.getType(), type); 57 | } 58 | 59 | @Override 60 | protected void handleUnmatchedItem( 61 | Builder mapBuilder, 62 | DependencyModel subsetItem) { 63 | String type = evaluateProperties(subsetItem.getType(), getHelper()); 64 | if ("pom".equals(type) && DependencyScope.IMPORT.equals(subsetItem.getScope())) { 65 | mapBuilder.put(subsetItem, subsetItem); 66 | } else { 67 | super.handleUnmatchedItem(mapBuilder, subsetItem); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/PedanticModuleOrderEnforcerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import java.util.Arrays; 19 | import org.junit.jupiter.api.BeforeEach; 20 | import org.junit.jupiter.api.Test; 21 | import org.junit.jupiter.api.TestInstance; 22 | import org.junit.jupiter.api.TestInstance.Lifecycle; 23 | import static org.assertj.core.api.Assertions.assertThat; 24 | import static org.mockito.Mockito.mock; 25 | import static org.mockito.Mockito.verify; 26 | import static org.mockito.Mockito.when; 27 | 28 | /** 29 | * JUnit tests for {@link PedanticModuleOrderEnforcer}. 30 | */ 31 | @TestInstance(Lifecycle.PER_CLASS) 32 | class PedanticModuleOrderEnforcerTest extends AbstractPedanticEnforcerTest { 33 | 34 | @Override 35 | PedanticModuleOrderEnforcer createRule() { 36 | return new PedanticModuleOrderEnforcer(this.mockMavenProject, this.mockHelper); 37 | } 38 | 39 | @BeforeEach 40 | void before() { 41 | when(this.mockMavenProject.getPackaging()).thenReturn("pom"); 42 | } 43 | 44 | @Override 45 | @Test 46 | void getDescription() { 47 | assertThat(this.testRule.getDescription()).isEqualTo(PedanticEnforcerRule.MODULE_ORDER); 48 | } 49 | 50 | @Override 51 | @Test 52 | void accept() { 53 | PedanticEnforcerVisitor visitor = mock(PedanticEnforcerVisitor.class); 54 | this.testRule.accept(visitor); 55 | 56 | verify(visitor).visit(this.testRule); 57 | } 58 | 59 | @Test 60 | void correctOrder() { 61 | when(this.projectModel.getModules()).thenReturn(Arrays.asList("m1", "m2", "m3")); 62 | 63 | executeRuleAndCheckReport(false); 64 | } 65 | 66 | @Test 67 | void correctOrderWithIgnores() { 68 | when(this.projectModel.getModules()).thenReturn(Arrays.asList("m9", "m8", "m1", "m2", "m7", "m3")); 69 | this.testRule.setIgnoredModules("m9,m8,m7"); 70 | 71 | executeRuleAndCheckReport(false); 72 | } 73 | 74 | @Test 75 | void noPomPackaging() { 76 | when(this.mockMavenProject.getPackaging()).thenReturn("jar"); 77 | when(this.projectModel.getModules()).thenReturn(null); 78 | 79 | executeRuleAndCheckReport(false); 80 | } 81 | 82 | @Test 83 | void incorrectOrder() { 84 | when(this.projectModel.getModules()).thenReturn(Arrays.asList("m2", "m1")); 85 | 86 | executeRuleAndCheckReport(true); 87 | } 88 | 89 | @Test 90 | void incorrectOrderWithIgnores() { 91 | when(this.projectModel.getModules()).thenReturn(Arrays.asList("m9", "m2", "m1")); 92 | this.testRule.setIgnoredModules("m9"); 93 | 94 | executeRuleAndCheckReport(true); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/PedanticPluginManagementLocationEnforcerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import org.junit.jupiter.api.BeforeEach; 19 | import org.junit.jupiter.api.Test; 20 | import com.github.ferstl.maven.pomenforcers.model.PluginModel; 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | import static org.mockito.Mockito.mock; 23 | import static org.mockito.Mockito.verify; 24 | import static org.mockito.Mockito.when; 25 | 26 | /** 27 | * JUnit tests for {@link PedanticPluginManagementLocationEnforcer}. 28 | */ 29 | class PedanticPluginManagementLocationEnforcerTest extends AbstractPedanticEnforcerTest { 30 | 31 | @Override 32 | PedanticPluginManagementLocationEnforcer createRule() { 33 | return new PedanticPluginManagementLocationEnforcer(this.mockMavenProject, this.mockHelper); 34 | } 35 | 36 | @BeforeEach 37 | void before() { 38 | when(this.mockMavenProject.getGroupId()).thenReturn("a.b.c"); 39 | when(this.mockMavenProject.getArtifactId()).thenReturn("parent"); 40 | this.projectModel.getManagedPlugins().add(new PluginModel("a.b.c", "a", "1.0")); 41 | } 42 | 43 | @Override 44 | @Test 45 | void getDescription() { 46 | assertThat(this.testRule.getDescription()).isEqualTo(PedanticEnforcerRule.PLUGIN_MANAGEMENT_LOCATION); 47 | } 48 | 49 | @Override 50 | @Test 51 | void accept() { 52 | PedanticEnforcerVisitor visitor = mock(PedanticEnforcerVisitor.class); 53 | this.testRule.accept(visitor); 54 | 55 | verify(visitor).visit(this.testRule); 56 | } 57 | 58 | @Test 59 | void noPluginManagingPomsDeclared() { 60 | executeRuleAndCheckReport(false); 61 | } 62 | 63 | @Test 64 | void isPluginManagingPom() { 65 | this.testRule.setPluginManagingPoms("a.b.c:parent"); 66 | 67 | executeRuleAndCheckReport(false); 68 | } 69 | 70 | @Test 71 | void isNotPluginManagingPom() { 72 | this.testRule.setPluginManagingPoms("some.other:pom"); 73 | 74 | executeRuleAndCheckReport(true); 75 | } 76 | 77 | void pluginManagementAllowedInParentPom() { 78 | when(this.mockMavenProject.getPackaging()).thenReturn("pom"); 79 | 80 | executeRuleAndCheckReport(true); 81 | } 82 | 83 | void pluginManagementNotAllowedInParentPom() { 84 | when(this.mockMavenProject.getPackaging()).thenReturn("pom"); 85 | this.testRule.setAllowParentPoms(false); 86 | 87 | executeRuleAndCheckReport(true); 88 | } 89 | 90 | @Test 91 | void pluginManagementInNonParentPom() { 92 | when(this.mockMavenProject.getPackaging()).thenReturn("jar"); 93 | 94 | executeRuleAndCheckReport(false); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/priority/CompoundPriorityOrdering.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.priority; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Arrays; 20 | import java.util.Comparator; 21 | import java.util.List; 22 | import java.util.Set; 23 | import com.google.common.collect.Iterables; 24 | import com.google.common.collect.LinkedHashMultimap; 25 | import com.google.common.collect.Lists; 26 | import com.google.common.collect.Multimap; 27 | import com.google.common.collect.Ordering; 28 | import com.google.common.collect.Sets; 29 | 30 | /** 31 | * @param Type of this ordering. 32 | * @param

Type of the priorities. 33 | * @param Type of the {@link PriorityOrderingFactory}. 34 | */ 35 | public class CompoundPriorityOrdering, F extends PriorityOrderingFactory> extends Ordering { 36 | 37 | private final Set orderBy; 38 | private final Multimap priorityMap; 39 | 40 | public static , F extends PriorityOrderingFactory> CompoundPriorityOrdering orderBy(Iterable artifactElements) { 41 | if (Iterables.isEmpty(artifactElements)) { 42 | throw new IllegalArgumentException("No order specified."); 43 | } 44 | return new CompoundPriorityOrdering<>(artifactElements); 45 | } 46 | 47 | @SafeVarargs 48 | public static , F extends PriorityOrderingFactory> 49 | CompoundPriorityOrdering orderBy(F... artifactElements) { 50 | return orderBy(Arrays.asList(artifactElements)); 51 | } 52 | 53 | private CompoundPriorityOrdering(Iterable artifactElements) { 54 | this.orderBy = Sets.newLinkedHashSet(artifactElements); 55 | this.priorityMap = LinkedHashMultimap.create(); 56 | } 57 | 58 | public void redefineOrderBy(Iterable artifactElements) { 59 | this.orderBy.clear(); 60 | this.orderBy.addAll(Lists.newArrayList(artifactElements)); 61 | } 62 | 63 | public void setPriorities(F artifactElement, Iterable

priorities) { 64 | this.priorityMap.removeAll(artifactElement); 65 | this.priorityMap.putAll(artifactElement, priorities); 66 | } 67 | 68 | @Override 69 | public int compare(T left, T right) { 70 | return createOrdering().compare(left, right); 71 | } 72 | 73 | private Ordering createOrdering() { 74 | List> comparators = new ArrayList<>(this.orderBy.size()); 75 | for (F artifactElement : this.orderBy) { 76 | Comparator comparator = 77 | artifactElement.createPriorityOrdering(this.priorityMap.get(artifactElement)); 78 | comparators.add(comparator); 79 | } 80 | 81 | return Ordering.compound(comparators); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/DependencyElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import java.util.Collection; 19 | import java.util.Map; 20 | import java.util.function.Function; 21 | import com.github.ferstl.maven.pomenforcers.priority.PriorityOrdering; 22 | import com.github.ferstl.maven.pomenforcers.priority.PriorityOrderingFactory; 23 | import com.google.common.collect.Maps; 24 | import static com.github.ferstl.maven.pomenforcers.model.functions.StringStartsWithEquivalence.stringStartsWith; 25 | import static java.util.Objects.requireNonNull; 26 | 27 | 28 | public enum DependencyElement implements PriorityOrderingFactory, Function { 29 | GROUP_ID("groupId") { 30 | @Override 31 | public PriorityOrdering createPriorityOrdering(Collection priorityCollection) { 32 | return new PriorityOrdering<>(priorityCollection, this, stringStartsWith()); 33 | } 34 | 35 | @Override 36 | public String apply(DependencyModel input) { 37 | return input.getGroupId(); 38 | } 39 | }, 40 | 41 | ARTIFACT_ID("artifactId") { 42 | @Override 43 | public PriorityOrdering createPriorityOrdering(Collection priorityCollection) { 44 | return new PriorityOrdering<>(priorityCollection, this, stringStartsWith()); 45 | } 46 | 47 | @Override 48 | public String apply(DependencyModel input) { 49 | return input.getArtifactId(); 50 | } 51 | }, 52 | 53 | SCOPE("scope") { 54 | @Override 55 | public PriorityOrdering createPriorityOrdering(Collection priorityCollection) { 56 | return new PriorityOrdering<>(priorityCollection, this); 57 | } 58 | 59 | @Override 60 | public String apply(DependencyModel input) { 61 | return input.getScope().getScopeName(); 62 | } 63 | }; 64 | 65 | private static final Map elementMap; 66 | 67 | static { 68 | elementMap = Maps.newLinkedHashMap(); 69 | for (DependencyElement element : values()) { 70 | elementMap.put(element.getElementName(), element); 71 | } 72 | } 73 | 74 | public static DependencyElement getByElementName(String elementName) { 75 | requireNonNull(elementName, "Element name is null"); 76 | 77 | DependencyElement result = elementMap.get(elementName); 78 | if (result == null) { 79 | throw new IllegalArgumentException("No dependency element with name " + elementName); 80 | } 81 | 82 | return result; 83 | } 84 | 85 | private final String elementName; 86 | 87 | DependencyElement(String elementName) { 88 | this.elementName = elementName; 89 | } 90 | 91 | public String getElementName() { 92 | return this.elementName; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/PedanticDependencyElementEnforcerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import java.nio.file.Path; 19 | import java.nio.file.Paths; 20 | import org.apache.maven.project.MavenProject; 21 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 22 | import org.junit.jupiter.api.BeforeEach; 23 | import org.junit.jupiter.api.Test; 24 | import org.w3c.dom.Document; 25 | import com.github.ferstl.maven.pomenforcers.model.ProjectModel; 26 | import com.github.ferstl.maven.pomenforcers.util.XmlUtils; 27 | import static com.github.ferstl.maven.pomenforcers.ErrorReportAssert.assertThat; 28 | import static org.mockito.Mockito.mock; 29 | 30 | class PedanticDependencyElementEnforcerTest { 31 | 32 | private ErrorReport errorReport; 33 | MavenProject mockMavenProject; 34 | ExpressionEvaluator mockHelper; 35 | 36 | @BeforeEach 37 | void before() { 38 | this.errorReport = new ErrorReport(PedanticEnforcerRule.DEPENDENCY_ELEMENT); 39 | this.mockHelper = mock(ExpressionEvaluator.class); 40 | this.mockMavenProject = mock(MavenProject.class); 41 | } 42 | 43 | @Test 44 | void defaultOrdering() { 45 | // arrange 46 | Path pomFile = Paths.get("src/test/projects/example-project/module1/pom.xml"); 47 | PedanticDependencyElementEnforcer enforcer = createEnforcer(pomFile); 48 | 49 | // act 50 | enforcer.doEnforce(this.errorReport); 51 | 52 | // assert 53 | assertThat(this.errorReport).hasNoErrors(); 54 | } 55 | 56 | @Test 57 | void customOrderingForDependencies() { 58 | // arrange 59 | Path pomFile = Paths.get("src/test/projects/example-project/module1/pom.xml"); 60 | PedanticDependencyElementEnforcer enforcer = createEnforcer(pomFile); 61 | 62 | enforcer.setElementPriorities("artifactId,groupId"); 63 | 64 | // act 65 | enforcer.doEnforce(this.errorReport); 66 | 67 | // assert 68 | assertThat(this.errorReport).hasErrors(); 69 | } 70 | 71 | @Test 72 | void customOrderingForDependencyManagement() { 73 | // arrange 74 | Path pomFile = Paths.get("src/test/projects/example-project/pom.xml"); 75 | PedanticDependencyElementEnforcer enforcer = createEnforcer(pomFile); 76 | 77 | enforcer.setElementPriorities("version"); 78 | 79 | // act 80 | enforcer.doEnforce(this.errorReport); 81 | 82 | // assert 83 | assertThat(this.errorReport).hasErrors(); 84 | } 85 | 86 | private PedanticDependencyElementEnforcer createEnforcer(Path pomFile) { 87 | Document document = XmlUtils.parseXml(pomFile.toFile()); 88 | PedanticDependencyElementEnforcer enforcer = new PedanticDependencyElementEnforcer(this.mockMavenProject, this.mockHelper); 89 | 90 | enforcer.initialize(document, new ProjectModel()); 91 | return enforcer; 92 | } 93 | 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/PedanticPluginElementEnforcerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import java.nio.file.Path; 19 | import java.nio.file.Paths; 20 | import org.apache.maven.project.MavenProject; 21 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; 22 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 23 | import org.junit.jupiter.api.BeforeEach; 24 | import org.junit.jupiter.api.Test; 25 | import org.w3c.dom.Document; 26 | import com.github.ferstl.maven.pomenforcers.model.ProjectModel; 27 | import com.github.ferstl.maven.pomenforcers.util.XmlUtils; 28 | import static com.github.ferstl.maven.pomenforcers.ErrorReportAssert.assertThat; 29 | import static org.mockito.Mockito.mock; 30 | 31 | class PedanticPluginElementEnforcerTest { 32 | 33 | private ErrorReport errorReport; 34 | private MavenProject mockMavenProject; 35 | private ExpressionEvaluator mockHelper; 36 | 37 | @BeforeEach 38 | void before() throws ExpressionEvaluationException { 39 | this.mockHelper = mock(ExpressionEvaluator.class); 40 | this.mockMavenProject = mock(MavenProject.class); 41 | this.errorReport = new ErrorReport(PedanticEnforcerRule.PLUGIN_ELEMENT); 42 | } 43 | 44 | @Test 45 | void defaultOrdering() { 46 | // arrange 47 | Path pomFile = Paths.get("src/test/projects/plugins/pom.xml"); 48 | PedanticPluginElementEnforcer enforcer = createEnforcer(pomFile); 49 | 50 | // act 51 | enforcer.doEnforce(this.errorReport); 52 | 53 | // assert 54 | assertThat(this.errorReport).hasNoErrors(); 55 | } 56 | 57 | @Test 58 | void customOrderingForPlugins() { 59 | // arrange 60 | Path pomFile = Paths.get("src/test/projects/plugins/pom.xml"); 61 | PedanticPluginElementEnforcer enforcer = createEnforcer(pomFile); 62 | 63 | enforcer.setElementPriorities("artifactId,groupId"); 64 | 65 | // act 66 | enforcer.doEnforce(this.errorReport); 67 | 68 | // assert 69 | assertThat(this.errorReport).hasErrors(); 70 | } 71 | 72 | @Test 73 | void customOrderingForPluginManagement() { 74 | // arrange 75 | Path pomFile = Paths.get("src/test/projects/plugins/pom.xml"); 76 | PedanticPluginElementEnforcer enforcer = createEnforcer(pomFile); 77 | 78 | enforcer.setElementPriorities("version"); 79 | 80 | // act 81 | enforcer.doEnforce(this.errorReport); 82 | 83 | // assert 84 | assertThat(this.errorReport).hasErrors(); 85 | } 86 | 87 | private PedanticPluginElementEnforcer createEnforcer(Path pomFile) { 88 | Document document = XmlUtils.parseXml(pomFile.toFile()); 89 | PedanticPluginElementEnforcer enforcer = new PedanticPluginElementEnforcer(this.mockMavenProject, this.mockHelper); 90 | 91 | enforcer.initialize(document, new ProjectModel()); 92 | return enforcer; 93 | } 94 | 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/PedanticDependencyManagementLocationEnforcerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import org.junit.jupiter.api.BeforeEach; 19 | import org.junit.jupiter.api.Test; 20 | import com.github.ferstl.maven.pomenforcers.model.DependencyModel; 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | import static org.mockito.Mockito.mock; 23 | import static org.mockito.Mockito.verify; 24 | import static org.mockito.Mockito.when; 25 | 26 | /** 27 | * JUnit tests for {@link PedanticDependencyManagementLocationEnforcer}. 28 | */ 29 | class PedanticDependencyManagementLocationEnforcerTest extends AbstractPedanticEnforcerTest { 30 | 31 | @Override 32 | PedanticDependencyManagementLocationEnforcer createRule() { 33 | return new PedanticDependencyManagementLocationEnforcer(this.mockMavenProject, this.mockHelper); 34 | } 35 | 36 | @BeforeEach 37 | void before() { 38 | when(this.mockMavenProject.getGroupId()).thenReturn("a.b.c"); 39 | when(this.mockMavenProject.getArtifactId()).thenReturn("parent"); 40 | this.projectModel.getManagedDependencies().add(new DependencyModel("a.b.c", "a", "1.0", null, null, null)); 41 | } 42 | 43 | @Override 44 | @Test 45 | void getDescription() { 46 | assertThat(this.testRule.getDescription()).isEqualTo(PedanticEnforcerRule.DEPENDENCY_MANAGEMENT_LOCATION); 47 | } 48 | 49 | @Override 50 | @Test 51 | void accept() { 52 | PedanticEnforcerVisitor visitor = mock(PedanticEnforcerVisitor.class); 53 | this.testRule.accept(visitor); 54 | 55 | verify(visitor).visit(this.testRule); 56 | } 57 | 58 | @Test 59 | void noDependencyManagingPomsDeclared() { 60 | executeRuleAndCheckReport(false); 61 | } 62 | 63 | @Test 64 | void noDependencyManagementDeclared() { 65 | this.projectModel.getManagedDependencies().clear(); 66 | 67 | executeRuleAndCheckReport(false); 68 | } 69 | 70 | @Test 71 | void isDependencyManagingPom() { 72 | this.testRule.setDependencyManagingPoms("a.b.c:parent"); 73 | 74 | executeRuleAndCheckReport(false); 75 | } 76 | 77 | @Test 78 | void isNotDependencyManagingPom() { 79 | this.testRule.setDependencyManagingPoms("some.other:pom"); 80 | 81 | executeRuleAndCheckReport(true); 82 | } 83 | 84 | void dependencyManagementAllowedInParentPom() { 85 | when(this.mockMavenProject.getPackaging()).thenReturn("pom"); 86 | 87 | executeRuleAndCheckReport(true); 88 | } 89 | 90 | void dependencyManagementNotAllowedInParentPom() { 91 | when(this.mockMavenProject.getPackaging()).thenReturn("pom"); 92 | this.testRule.setAllowParentPoms(false); 93 | 94 | executeRuleAndCheckReport(true); 95 | } 96 | 97 | @Test 98 | void dependencyManagementInNonParentPom() { 99 | when(this.mockMavenProject.getPackaging()).thenReturn("jar"); 100 | 101 | executeRuleAndCheckReport(false); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/PedanticDependencyOrderEnforcer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import java.util.Collection; 19 | import javax.inject.Inject; 20 | import javax.inject.Named; 21 | import org.apache.maven.model.Dependency; 22 | import org.apache.maven.project.MavenProject; 23 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 24 | import com.github.ferstl.maven.pomenforcers.model.DependencyModel; 25 | 26 | 27 | /** 28 | * This enforcer makes sure that all artifacts in your dependencies section are 29 | * ordered. The ordering can be defined by any combination of scope, groupId 30 | * and artifactId. Each of these attributes may be given a priority. 31 | *

32 |  * ### Example
33 |  *     <rules>
34 |  *       <dependencyOrder implementation="com.github.ferstl.maven.pomenforcers.PedanticDependencyOrderEnforcer">
35 |  *         <!-- order by scope, groupId and artifactId (default) -->
36 |  *         <orderBy>scope,groupId,artifactId</orderBy>
37 |  *         <!-- runtime scope should occur before provided scope -->
38 |  *         <scopePriorities>compile,runtime,provided</scopePriorities>
39 |  *         <!-- all group IDs starting with com.myproject and com.mylibs should occur first -->
40 |  *         <groupIdPriorities>com.myproject,com.mylibs</groupIdPriorities>
41 |  *         <!-- all artifact IDs starting with commons- and utils- should occur first -->
42 |  *         <artifactIdPriorities>commons-,utils-</artifactIdPriorities>
43 |  *       </dependencyOrder>
44 |  *     </rules>
45 |  * 
46 | * 47 | * @id {@link PedanticEnforcerRule#DEPENDENCY_ORDER} 48 | * @since 1.0.0 49 | */ 50 | @Named("dependencyOrder") 51 | public class PedanticDependencyOrderEnforcer extends AbstractPedanticDependencyOrderEnforcer { 52 | 53 | @Inject 54 | public PedanticDependencyOrderEnforcer(final MavenProject project, final ExpressionEvaluator helper) { 55 | super(project, helper); 56 | } 57 | 58 | @Override 59 | protected void accept(PedanticEnforcerVisitor visitor) { 60 | visitor.visit(this); 61 | } 62 | 63 | @Override 64 | protected PedanticEnforcerRule getDescription() { 65 | return PedanticEnforcerRule.DEPENDENCY_ORDER; 66 | } 67 | 68 | @Override 69 | protected Collection getDeclaredDependencies() { 70 | return getProjectModel().getDependencies(); 71 | } 72 | 73 | @Override 74 | protected Collection getMavenDependencies(MavenProject project) { 75 | return project.getDependencies(); 76 | } 77 | 78 | @Override 79 | protected void reportError(ErrorReport report, Collection resolvedDependencies, Collection sortedDependencies) { 80 | 81 | report.addLine("Your dependencies have to be sorted this way:") 82 | .emptyLine() 83 | .addDiffUsingToString(resolvedDependencies, sortedDependencies, "Actual Order", "Required Order"); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/AbstractPedanticEnforcer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import java.util.Objects; 19 | import javax.xml.bind.JAXB; 20 | import org.apache.maven.enforcer.rule.api.AbstractEnforcerRule; 21 | import org.apache.maven.enforcer.rule.api.EnforcerLevel; 22 | import org.apache.maven.enforcer.rule.api.EnforcerRuleException; 23 | import org.apache.maven.project.MavenProject; 24 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 25 | import org.w3c.dom.Document; 26 | import com.github.ferstl.maven.pomenforcers.model.ProjectModel; 27 | import com.github.ferstl.maven.pomenforcers.util.XmlUtils; 28 | 29 | public abstract class AbstractPedanticEnforcer extends AbstractEnforcerRule { 30 | 31 | private final ExpressionEvaluator helper; 32 | 33 | private final MavenProject project; 34 | 35 | private Document pom; 36 | private ProjectModel projectModel; 37 | 38 | /** 39 | * If set to true, the enforcer rule will only issue a warning in the log and not fail the build. 40 | * Enabling this option is a good way to start using the enforcer rules in an already existing project. 41 | * 42 | * @configParam 43 | * @default false 44 | * @since 2.0.0 45 | */ 46 | private boolean warnOnly; 47 | 48 | public AbstractPedanticEnforcer(final MavenProject project, final ExpressionEvaluator helper) { 49 | this.project = Objects.requireNonNull(project); 50 | this.helper = Objects.requireNonNull(helper); 51 | } 52 | 53 | @Override 54 | public final void execute() throws EnforcerRuleException { 55 | Document pom = XmlUtils.parseXml(project.getFile()); 56 | ProjectModel model = JAXB.unmarshal(project.getFile(), ProjectModel.class); 57 | 58 | initialize(pom, model); 59 | 60 | ErrorReport report = new ErrorReport(getDescription()); 61 | doEnforce(report); 62 | 63 | if (report.hasErrors()) { 64 | throw new EnforcerRuleException(report.toString()); 65 | } 66 | } 67 | 68 | /** 69 | * Initialization method. Use this method when the enforcer rule is not instantiated by the 70 | * maven-enforcer-plugin. 71 | * 72 | * @param helper Enforcer rule helper. 73 | * @param pom POM Document. 74 | * @param projectModel Project model. 75 | */ 76 | void initialize(Document pom, ProjectModel projectModel) { 77 | this.pom = pom; 78 | this.projectModel = projectModel; 79 | } 80 | 81 | protected ExpressionEvaluator getHelper() { 82 | return this.helper; 83 | } 84 | 85 | protected MavenProject getMavenProject() { 86 | return this.project; 87 | } 88 | 89 | protected Document getPom() { 90 | return this.pom; 91 | } 92 | 93 | protected ProjectModel getProjectModel() { 94 | return this.projectModel; 95 | } 96 | 97 | protected abstract PedanticEnforcerRule getDescription(); 98 | 99 | protected abstract void doEnforce(ErrorReport report); 100 | 101 | protected abstract void accept(PedanticEnforcerVisitor visitor); 102 | 103 | @Override 104 | public EnforcerLevel getLevel() { 105 | return this.warnOnly ? EnforcerLevel.WARN : EnforcerLevel.ERROR; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/DependencyModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | 19 | import java.util.Collections; 20 | import java.util.List; 21 | import java.util.Objects; 22 | import javax.xml.bind.annotation.XmlElement; 23 | import javax.xml.bind.annotation.XmlElementWrapper; 24 | import javax.xml.bind.annotation.XmlRootElement; 25 | import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 26 | import com.google.common.base.Joiner; 27 | import static com.google.common.base.Objects.equal; 28 | 29 | @XmlRootElement(name = "dependency") 30 | public class DependencyModel extends ArtifactModel { 31 | 32 | private static final Joiner TO_STRING_JOINER = Joiner.on(":").skipNulls(); 33 | 34 | @XmlElement(namespace = "http://maven.apache.org/POM/4.0.0") 35 | @XmlJavaTypeAdapter(value = DependencyScopeAdapter.class) 36 | private DependencyScope scope; 37 | 38 | @XmlElement(namespace = "http://maven.apache.org/POM/4.0.0") 39 | private String classifier; 40 | 41 | @XmlElement(namespace = "http://maven.apache.org/POM/4.0.0") 42 | private String type; 43 | 44 | @XmlElementWrapper(namespace = "http://maven.apache.org/POM/4.0.0") 45 | @XmlElement(name = "exclusion", namespace = "http://maven.apache.org/POM/4.0.0") 46 | private List exclusions; 47 | 48 | // Constructor used by JAXB 49 | DependencyModel() { 50 | } 51 | 52 | public DependencyModel( 53 | String groupId, String artifactId, String version, String scope, String classifier, String type) { 54 | 55 | super(groupId, artifactId, version); 56 | this.scope = scope != null ? DependencyScope.getByScopeName(scope) : null; 57 | this.classifier = classifier; 58 | this.type = type; 59 | } 60 | 61 | public DependencyScope getScope() { 62 | return this.scope != null ? this.scope : DependencyScope.COMPILE; 63 | } 64 | 65 | public String getClassifier() { 66 | return this.classifier; 67 | } 68 | 69 | public String getType() { 70 | return this.type != null ? this.type : "jar"; 71 | } 72 | 73 | public List getExclusions() { 74 | return this.exclusions != null ? this.exclusions : Collections.emptyList(); 75 | } 76 | 77 | @Override 78 | public String toString() { 79 | return TO_STRING_JOINER.join( 80 | super.toString(), 81 | getType(), 82 | getScope().getScopeName(), 83 | this.classifier); 84 | } 85 | 86 | // Note that this equals() implementation breaks the symmetry contract! 87 | @Override 88 | public boolean equals(Object obj) { 89 | if (this == obj) { 90 | return true; 91 | } 92 | if (!(obj instanceof DependencyModel)) { 93 | return false; 94 | } 95 | 96 | DependencyModel other = (DependencyModel) obj; 97 | return super.equals(other) 98 | && equal(this.classifier, other.classifier) 99 | && equal(this.type, other.type) 100 | && equal(this.scope, other.scope) 101 | && equal(this.exclusions, other.exclusions); 102 | } 103 | 104 | @Override 105 | public int hashCode() { 106 | return Objects.hash(super.hashCode(), this.classifier, this.type, this.scope); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/PedanticPluginManagementOrderEnforcerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import org.apache.maven.model.Plugin; 19 | import org.junit.jupiter.api.Test; 20 | import com.github.ferstl.maven.pomenforcers.model.PluginModel; 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | import static org.mockito.Mockito.mock; 23 | import static org.mockito.Mockito.verify; 24 | 25 | /** 26 | * JUnit tests for {@link PedanticPluginManagementOrderEnforcer}. 27 | */ 28 | class PedanticPluginManagementOrderEnforcerTest extends AbstractPedanticEnforcerTest { 29 | 30 | @Override 31 | PedanticPluginManagementOrderEnforcer createRule() { 32 | return new PedanticPluginManagementOrderEnforcer(this.mockMavenProject, this.mockHelper); 33 | } 34 | 35 | @Test 36 | @Override 37 | void getDescription() { 38 | assertThat(this.testRule.getDescription()).isEqualTo(PedanticEnforcerRule.PLUGIN_MANAGEMENT_ORDER); 39 | } 40 | 41 | @Test 42 | @Override 43 | void accept() { 44 | PedanticEnforcerVisitor visitor = mock(PedanticEnforcerVisitor.class); 45 | this.testRule.accept(visitor); 46 | 47 | verify(visitor).visit(this.testRule); 48 | } 49 | 50 | @Test 51 | void defaultSettingsCorrect() { 52 | addManagedPlugin("a.b.c", "a"); 53 | addManagedPlugin("a.b.c", "b"); 54 | 55 | executeRuleAndCheckReport(false); 56 | } 57 | 58 | @Test 59 | void defaultSettingsWrongGroupIdOrder() { 60 | addManagedPlugin("d.e.f", "a"); 61 | addManagedPlugin("a.b.c", "a"); 62 | 63 | executeRuleAndCheckReport(true); 64 | } 65 | 66 | @Test 67 | void defaultSettingsWrongArtifactIdOrder() { 68 | addManagedPlugin("a.b.c", "b"); 69 | addManagedPlugin("a.b.c", "a"); 70 | 71 | executeRuleAndCheckReport(true); 72 | } 73 | 74 | @Test 75 | void groupIdPriorities() { 76 | this.testRule.setGroupIdPriorities("x.y.z,u.v.w"); 77 | 78 | addManagedPlugin("x.y.z", "a"); 79 | addManagedPlugin("u.v.w", "a"); 80 | addManagedPlugin("a.b.c", "a"); 81 | 82 | executeRuleAndCheckReport(false); 83 | } 84 | 85 | @Test 86 | void artifactIdPriorities() { 87 | this.testRule.setArtifactIdPriorities("z,y"); 88 | 89 | addManagedPlugin("a.b.c", "z"); 90 | addManagedPlugin("a.b.c", "y"); 91 | addManagedPlugin("a.b.c", "a"); 92 | 93 | executeRuleAndCheckReport(false); 94 | } 95 | 96 | @Test 97 | void orderBy() { 98 | this.testRule.setOrderBy("artifactId,groupId"); 99 | 100 | addManagedPlugin("x.y.z", "a"); 101 | addManagedPlugin("a.b.c", "b"); 102 | 103 | executeRuleAndCheckReport(false); 104 | } 105 | 106 | private void addManagedPlugin(String groupId, String artifactId) { 107 | String defaultVersion = "1.0"; 108 | PluginModel pluginModel = new PluginModel(groupId, artifactId, defaultVersion); 109 | Plugin mavenPlugin = new Plugin(); 110 | mavenPlugin.setGroupId(groupId); 111 | mavenPlugin.setArtifactId(artifactId); 112 | mavenPlugin.setVersion(defaultVersion); 113 | 114 | this.projectModel.getManagedPlugins().add(pluginModel); 115 | this.mockMavenProject.getPluginManagement().getPlugins().add(mavenPlugin); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/PedanticPomEnforcersIntegrationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import java.io.File; 19 | import org.junit.jupiter.api.extension.RegisterExtension; 20 | import io.takari.maven.testing.TestResources5; 21 | import io.takari.maven.testing.executor.MavenExecutionResult; 22 | import io.takari.maven.testing.executor.MavenRuntime; 23 | import io.takari.maven.testing.executor.MavenVersions; 24 | import io.takari.maven.testing.executor.junit.MavenPluginTest; 25 | 26 | // Enforcer plugin requires at least Maven version 3.6.3 27 | @MavenVersions({"3.9.11", "3.6.3"}) 28 | class PedanticPomEnforcersIntegrationTest { 29 | 30 | @RegisterExtension 31 | public final TestResources5 resources = new TestResources5(); 32 | 33 | private final MavenRuntime mavenRuntime; 34 | 35 | 36 | PedanticPomEnforcersIntegrationTest(MavenRuntime.MavenRuntimeBuilder builder) throws Exception { 37 | this.mavenRuntime = builder 38 | .withCliOptions("-B") 39 | .build(); 40 | } 41 | 42 | @MavenPluginTest 43 | void simpleProject() throws Exception { 44 | File basedir = this.resources.getBasedir("simple-project"); 45 | MavenExecutionResult result = this.mavenRuntime 46 | .forProject(basedir) 47 | .execute("enforcer:enforce"); 48 | 49 | result.assertErrorFreeLog(); 50 | } 51 | 52 | @MavenPluginTest 53 | void exampleProject() throws Exception { 54 | File basedir = this.resources.getBasedir("example-project"); 55 | MavenExecutionResult result = this.mavenRuntime 56 | .forProject(basedir) 57 | .execute("package", "enforcer:enforce"); 58 | 59 | result.assertErrorFreeLog(); 60 | } 61 | 62 | @MavenPluginTest 63 | void issue2() throws Exception { 64 | File basedir = this.resources.getBasedir("issue-2"); 65 | MavenExecutionResult result = this.mavenRuntime 66 | .forProject(basedir) 67 | .execute("enforcer:enforce"); 68 | 69 | result.assertErrorFreeLog(); 70 | } 71 | 72 | @MavenPluginTest 73 | void issue23() throws Exception { 74 | File basedir = this.resources.getBasedir("issue-23"); 75 | MavenExecutionResult result = this.mavenRuntime 76 | .forProject(basedir) 77 | .execute("enforcer:enforce"); 78 | 79 | result.assertLogText("Dependency exclusions have to be declared in "); 80 | result.assertLogText("BUILD FAILURE"); 81 | } 82 | 83 | @MavenPluginTest 84 | void issue53() throws Exception { 85 | File basedir = this.resources.getBasedir("issue-53"); 86 | MavenExecutionResult result = this.mavenRuntime 87 | .forProject(basedir) 88 | .execute("enforcer:enforce"); 89 | 90 | result.assertLogText("Dependency exclusions have to be declared in "); 91 | result.assertLogText("BUILD FAILURE"); 92 | } 93 | 94 | @MavenPluginTest 95 | void warnOnly() throws Exception { 96 | File basedir = this.resources.getBasedir("warn-only"); 97 | MavenExecutionResult result = this.mavenRuntime 98 | .forProject(basedir) 99 | .execute("enforcer:enforce"); 100 | 101 | result.assertErrorFreeLog(); 102 | result.assertLogText("POM_SECTION_ORDER: "); 103 | result.assertLogText("DEPENDENCY_ORDER: "); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/priority/PriorityOrdering.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.priority; 17 | 18 | import java.util.Collection; 19 | import java.util.LinkedHashSet; 20 | import java.util.List; 21 | import java.util.function.Function; 22 | import com.google.common.base.Equivalence; 23 | import com.google.common.collect.Ordering; 24 | 25 | /** 26 | * Comparator that makes comparisons based on a priority collection. Objects that match an item in 27 | * the priority collection will be considered smaller than objects that don't match any item in the 28 | * priority collection. If both compared objects match different items in the priority collection, 29 | * the object that matches the item closer to the beginning of the collection (as 30 | * returned by the collection's iterator) will be considered smaller. Thus, it is recommended to use 31 | * {@link List}s or {@link LinkedHashSet}s to define the priority collection. 32 | * 33 | * @param

Type of the priority collection. 34 | * @param Type of the values to be compared. 35 | */ 36 | public class PriorityOrdering

, T> extends Ordering { 37 | 38 | /** 39 | * The priority collection. 40 | */ 41 | private final Collection

priorityCollection; 42 | 43 | /** 44 | * Matches the values to be compared with the items in the priority collection. 45 | */ 46 | private final Equivalence priorityMatcher; 47 | 48 | /** 49 | * Transforms the type of the objects to be compared into the type of the priority collection. Use 50 | * {@link Function#identity()} if the type of the priority collection and the type of the objects to be 51 | * compared are the same. 52 | */ 53 | private final Function transformer; 54 | 55 | 56 | public PriorityOrdering(Collection

prioritizedItems, Function transformer, Equivalence priorityMatcher) { 57 | this.priorityCollection = prioritizedItems; 58 | this.priorityMatcher = priorityMatcher; 59 | this.transformer = transformer; 60 | } 61 | 62 | public PriorityOrdering(Collection

priorityCollection, Function transformer) { 63 | this(priorityCollection, transformer, Equivalence.equals()); 64 | } 65 | 66 | @Override 67 | public int compare(T object1, T object2) { 68 | P comparable1 = this.transformer.apply(object1); 69 | P comparable2 = this.transformer.apply(object2); 70 | 71 | int rank1 = this.rank(comparable1); 72 | int rank2 = this.rank(comparable2); 73 | 74 | if (rank1 == rank2) { 75 | return comparable1.compareTo(comparable2); 76 | } 77 | 78 | return rank1 - rank2; 79 | 80 | } 81 | 82 | /** 83 | * Determine the priority of the given item by matching it against the priority collection. 84 | * The lower the rank, the higher the priority. 85 | * 86 | * @param item The item to prioritize. 87 | * @return The priority of the given item or {@link Integer#MAX_VALUE} if the given item does not 88 | * match any element of the priority collection. 89 | */ 90 | private int rank(P item) { 91 | int i = 0; 92 | for (P prioritizedItem : this.priorityCollection) { 93 | if (this.priorityMatcher.equivalent(item, prioritizedItem)) { 94 | return i; 95 | } 96 | i++; 97 | } 98 | 99 | return Integer.MAX_VALUE; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/PedanticDependencyManagementOrderEnforcer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import java.util.Collection; 19 | import java.util.Collections; 20 | import javax.inject.Inject; 21 | import javax.inject.Named; 22 | import org.apache.maven.model.Dependency; 23 | import org.apache.maven.model.DependencyManagement; 24 | import org.apache.maven.project.MavenProject; 25 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 26 | import com.github.ferstl.maven.pomenforcers.model.DependencyModel; 27 | 28 | 29 | /** 30 | * This enforcer makes sure that all artifacts in your dependency management are 31 | * ordered. The ordering can be defined by any combination of scope, 32 | * groupId and artifactId. Each of these attributes 33 | * may be given a priority. 34 | *

35 |  * ### Example
36 |  *     <rules>
37 |  *       <dependencyManagementOrder implementation="com.github.ferstl.maven.pomenforcers.PedanticDependencyManagementOrderEnforcer">
38 |  *         <!-- order by scope, groupId and artifactId (default) -->
39 |  *         <orderBy>scope,groupId,artifactId</orderBy>
40 |  *         <!-- runtime scope should occur before provided scope -->
41 |  *         <scopePriorities>compile,runtime,provided</scopePriorities>
42 |  *         <!-- all group IDs starting with com.myproject and com.mylibs should occur first -->
43 |  *         <groupIdPriorities>com.myproject,com.mylibs</groupIdPriorities>
44 |  *         <!-- all artifact IDs starting with commons- and utils- should occur first -->
45 |  *         <artifactIdPriorities>commons-,utils-</artifactIdPriorities>
46 |  *       </dependencyManagementOrder>
47 |  *     </rules>
48 |  * 
49 | * 50 | * @id {@link PedanticEnforcerRule#DEPENDENCY_MANAGEMENT_ORDER} 51 | * @since 1.0.0 52 | */ 53 | @Named("dependencyManagementOrder") 54 | public class PedanticDependencyManagementOrderEnforcer extends AbstractPedanticDependencyOrderEnforcer { 55 | 56 | @Inject 57 | public PedanticDependencyManagementOrderEnforcer(final MavenProject project, final ExpressionEvaluator helper) { 58 | super(project, helper); 59 | } 60 | 61 | @Override 62 | protected PedanticEnforcerRule getDescription() { 63 | return PedanticEnforcerRule.DEPENDENCY_MANAGEMENT_ORDER; 64 | } 65 | 66 | @Override 67 | protected void accept(PedanticEnforcerVisitor visitor) { 68 | visitor.visit(this); 69 | } 70 | 71 | @Override 72 | protected Collection getDeclaredDependencies() { 73 | return getProjectModel().getManagedDependencies(); 74 | } 75 | 76 | @Override 77 | protected Collection getMavenDependencies(MavenProject project) { 78 | DependencyManagement dependencyManagement = project.getDependencyManagement(); 79 | if (dependencyManagement != null) { 80 | return dependencyManagement.getDependencies(); 81 | } else { 82 | return Collections.emptyList(); 83 | } 84 | } 85 | 86 | @Override 87 | protected void reportError(ErrorReport report, Collection resolvedDependencies, Collection sortedDependencies) { 88 | 89 | report.addLine("Your dependency management has to be ordered this way:") 90 | .emptyLine() 91 | .addDiffUsingToString(resolvedDependencies, sortedDependencies, "Actual Order", "Required Order"); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/ProjectModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import java.util.Collections; 19 | import java.util.List; 20 | import java.util.Objects; 21 | import javax.xml.bind.annotation.XmlElement; 22 | import javax.xml.bind.annotation.XmlElementWrapper; 23 | import javax.xml.bind.annotation.XmlRootElement; 24 | import com.google.common.base.Joiner; 25 | 26 | @XmlRootElement(name = "project") 27 | public class ProjectModel { 28 | 29 | private static final Joiner TO_STRING_JOINER = Joiner.on("\n").skipNulls(); 30 | 31 | @XmlElement(namespace = "http://maven.apache.org/POM/4.0.0") 32 | public String groupId; 33 | @XmlElement(namespace = "http://maven.apache.org/POM/4.0.0") 34 | public String artifactId; 35 | 36 | @XmlElementWrapper(namespace = "http://maven.apache.org/POM/4.0.0") 37 | @XmlElement(name = "module", namespace = "http://maven.apache.org/POM/4.0.0") 38 | public List modules; 39 | 40 | @XmlElement(namespace = "http://maven.apache.org/POM/4.0.0") 41 | public DependencyManagementModel dependencyManagement; 42 | 43 | @XmlElement(namespace = "http://maven.apache.org/POM/4.0.0") 44 | public DependenciesModel dependencies; 45 | 46 | @XmlElement(namespace = "http://maven.apache.org/POM/4.0.0") 47 | public BuildModel build; 48 | 49 | public List getModules() { 50 | return this.modules != null ? this.modules : Collections.emptyList(); 51 | } 52 | 53 | public List getManagedDependencies() { 54 | return this.dependencyManagement != null ? 55 | this.dependencyManagement.getDependencies() : Collections.emptyList(); 56 | } 57 | 58 | public List getDependencies() { 59 | return this.dependencies != null ? 60 | this.dependencies.getDependencies() : Collections.emptyList(); 61 | } 62 | 63 | public List getManagedPlugins() { 64 | return this.build != null ? this.build.getManagedPlugins() : Collections.emptyList(); 65 | } 66 | 67 | public List getPlugins() { 68 | return this.build != null ? this.build.getPlugins() : Collections.emptyList(); 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | StringBuilder sb = new StringBuilder("Project ") 74 | .append(this.groupId) 75 | .append(":") 76 | .append(this.artifactId) 77 | .append(" [\n"); 78 | return TO_STRING_JOINER 79 | .appendTo( 80 | sb, 81 | CollectionToStringHelper.toString("Modules", this.modules), 82 | this.dependencyManagement, 83 | this.dependencies, 84 | this.build) 85 | .append("\n]") 86 | .toString(); 87 | } 88 | 89 | @Override 90 | public boolean equals(Object obj) { 91 | if (obj == this) { 92 | return true; 93 | } 94 | if (!(obj instanceof ProjectModel)) { 95 | return false; 96 | } 97 | ProjectModel other = (ProjectModel) obj; 98 | return Objects.equals(this.groupId, other.groupId) 99 | && Objects.equals(this.artifactId, other.artifactId) 100 | && Objects.equals(this.modules, other.modules) 101 | && Objects.equals(this.dependencyManagement, other.dependencyManagement) 102 | && Objects.equals(this.dependencies, other.dependencies) 103 | && Objects.equals(this.build, other.build); 104 | } 105 | 106 | @Override 107 | public int hashCode() { 108 | return Objects.hash( 109 | this.groupId, this.artifactId, this.modules, this.dependencyManagement, this.dependencies, this.build); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/ErrorReport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import java.util.Collection; 19 | import java.util.LinkedList; 20 | import java.util.function.Function; 21 | import java.util.stream.Collectors; 22 | import com.github.ferstl.maven.pomenforcers.util.SideBySideDiffUtil; 23 | import com.google.common.base.Joiner; 24 | import com.google.common.base.Strings; 25 | import com.google.common.collect.Collections2; 26 | import static com.google.common.base.Functions.toStringFunction; 27 | 28 | 29 | public class ErrorReport { 30 | 31 | private static final String LIST_ITEM = "- "; 32 | private static final String LINE_SEPARATOR = System.getProperty("line.separator", "\n"); 33 | private static final Joiner LINE_JOINER = Joiner.on(LINE_SEPARATOR); 34 | private static final Joiner LIST_JOINER = Joiner.on(LINE_SEPARATOR + LIST_ITEM); 35 | 36 | private final String title; 37 | private final Collection lines; 38 | 39 | private boolean useLargeTitle; 40 | 41 | public static String toList(Collection collection) { 42 | return toList(collection, Function.identity()); 43 | } 44 | 45 | public static String toList(Collection collection, Function toStringFunction) { 46 | return LIST_ITEM + LIST_JOINER.join(Collections2.transform(collection, toStringFunction::apply)); 47 | } 48 | 49 | public ErrorReport(PedanticEnforcerRule rule) { 50 | this.title = rule.name() + ": " + rule.getSlogan(); 51 | this.lines = new LinkedList<>(); 52 | } 53 | 54 | public ErrorReport useLargeTitle() { 55 | this.useLargeTitle = true; 56 | return this; 57 | } 58 | 59 | public ErrorReport addLine(Object line) { 60 | this.lines.add(line); 61 | return this; 62 | } 63 | 64 | public ErrorReport addDiff(Collection actual, Collection required, String leftTitle, String rightTitle) { 65 | String diff = SideBySideDiffUtil.diff(actual, required, leftTitle, rightTitle); 66 | this.lines.add(diff); 67 | return this; 68 | } 69 | 70 | public ErrorReport addDiff(Collection actual, Collection required, String leftTitle, String rightTitle, Function toStringFunction) { 71 | Collection actualAsString = actual.stream().map(toStringFunction).collect(Collectors.toList()); 72 | Collection requiredAsString = required.stream().map(toStringFunction).collect(Collectors.toList()); 73 | 74 | return addDiff(actualAsString, requiredAsString, leftTitle, rightTitle); 75 | } 76 | 77 | public ErrorReport addDiffUsingToString(Collection actual, Collection required, String leftTitle, String rightTitle) { 78 | return addDiff(actual, required, leftTitle, rightTitle, toStringFunction()); 79 | } 80 | 81 | public ErrorReport formatLine(String line, Object... params) { 82 | this.lines.add(String.format(line, params)); 83 | return this; 84 | } 85 | 86 | public ErrorReport emptyLine() { 87 | this.lines.add(""); 88 | return this; 89 | } 90 | 91 | public boolean hasErrors() { 92 | return !this.lines.isEmpty(); 93 | } 94 | 95 | @Override 96 | public String toString() { 97 | return LINE_JOINER.join( 98 | formatTitle(), 99 | LINE_JOINER.join(this.lines)); 100 | } 101 | 102 | private String formatTitle() { 103 | if (this.useLargeTitle) { 104 | String border = Strings.repeat("#", this.title.length() + 4); 105 | return LINE_JOINER.join("", border, "# " + this.title + " #", border, ""); 106 | } 107 | return LINE_JOINER.join("" + this.title, Strings.repeat("=", this.title.length()), ""); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/model/ArtifactModelTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import org.junit.jupiter.api.Test; 19 | import static org.junit.jupiter.api.Assertions.assertEquals; 20 | import static org.junit.jupiter.api.Assertions.assertFalse; 21 | import static org.junit.jupiter.api.Assertions.assertThrows; 22 | import static org.junit.jupiter.api.Assertions.assertTrue; 23 | 24 | 25 | class ArtifactModelTest { 26 | 27 | @Test 28 | void constructorWithInvalidWildcard() { 29 | assertThrows(IllegalArgumentException.class, () -> new ArtifactModel("group", "invalid*wildcard*positions")); 30 | } 31 | 32 | @Test 33 | void toStringNoGroupId() { 34 | ArtifactModel model = new ArtifactModel(null, "artifact", "1.0.0"); 35 | 36 | assertEquals(":artifact:1.0.0", model.toString()); 37 | } 38 | 39 | @Test 40 | void toStringNoVersion() { 41 | ArtifactModel model = new ArtifactModel("group", "artifact"); 42 | 43 | assertEquals("group:artifact:", model.toString()); 44 | } 45 | 46 | @Test 47 | void toStringNoGroupIdAndNoVersion() { 48 | ArtifactModel model = new ArtifactModel(null, "artifact"); 49 | 50 | assertEquals(":artifact:", model.toString()); 51 | } 52 | 53 | @Test 54 | void matchesWithFullWildcard() { 55 | wildcardTest("something", "*", true); 56 | } 57 | 58 | @Test 59 | void matchesWithLeadingWildcard() { 60 | wildcardTest("prefix-foo", "*-foo", true); 61 | } 62 | 63 | @Test 64 | void matchesWithTrailingWildcard() { 65 | wildcardTest("foo-suffix", "foo-*", true); 66 | } 67 | 68 | @Test 69 | void matchesWithContainsWildcard() { 70 | wildcardTest("foo-contains-bar", "*contains*", true); 71 | wildcardTest("foo-something-bar", "*contains*", false); 72 | } 73 | 74 | @Test 75 | void matchesWithMismatchingGroupId() { 76 | ArtifactModel model = new ArtifactModel("group", "artifact"); 77 | ArtifactModel pattern = new ArtifactModel("*", "different-artifact"); 78 | 79 | assertFalse(model.matches(pattern)); 80 | } 81 | 82 | @Test 83 | void matchesWithMismatchingArtifactId() { 84 | ArtifactModel model = new ArtifactModel("group", "artifact"); 85 | ArtifactModel pattern = new ArtifactModel("different-group", "*"); 86 | 87 | assertFalse(model.matches(pattern)); 88 | } 89 | 90 | @Test 91 | void matchesOnPatterns() { 92 | ArtifactModel pattern1 = new ArtifactModel("*", "artifact"); 93 | ArtifactModel pattern1Copy = new ArtifactModel("*", "artifact"); 94 | ArtifactModel pattern2 = new ArtifactModel("group", "*"); 95 | 96 | assertFalse(pattern1.matches(pattern2)); 97 | assertTrue(pattern1.matches(pattern1Copy)); 98 | } 99 | 100 | @Test 101 | void matchesOnSelf() { 102 | ArtifactModel pattern = new ArtifactModel("*", "artifact"); 103 | 104 | assertTrue(pattern.matches(pattern)); 105 | } 106 | 107 | @Test 108 | void matchesOnNull() { 109 | ArtifactModel pattern = new ArtifactModel("*", "artifact"); 110 | 111 | assertFalse(pattern.matches(null)); 112 | } 113 | 114 | private void wildcardTest(String string, String pattern, boolean shouldMatch) { 115 | ArtifactModel groupIdModel = new ArtifactModel(string, "artifact"); 116 | ArtifactModel groupIdPattern = new ArtifactModel(pattern, "artifact"); 117 | 118 | ArtifactModel artifactIdModel = new ArtifactModel("group", string); 119 | ArtifactModel artifactIdPattern = new ArtifactModel("group", pattern); 120 | 121 | assertEquals(shouldMatch, artifactIdModel.matches(artifactIdPattern)); 122 | assertEquals(shouldMatch, groupIdModel.matches(groupIdPattern)); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/PedanticPomSectionOrderEnforcerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Arrays; 20 | import java.util.Collection; 21 | import java.util.List; 22 | import org.junit.jupiter.api.Test; 23 | import org.w3c.dom.Document; 24 | import org.w3c.dom.Element; 25 | import com.github.ferstl.maven.pomenforcers.model.PomSection; 26 | import static org.assertj.core.api.Assertions.assertThat; 27 | import static org.mockito.Mockito.mock; 28 | import static org.mockito.Mockito.verify; 29 | 30 | /** 31 | * JUnit tests for {@link PedanticPomSectionOrderEnforcer}. 32 | */ 33 | class PedanticPomSectionOrderEnforcerTest extends AbstractPedanticEnforcerTest { 34 | 35 | @Override 36 | PedanticPomSectionOrderEnforcer createRule() { 37 | return new PedanticPomSectionOrderEnforcer(this.mockMavenProject, this.mockHelper); 38 | } 39 | 40 | @Override 41 | @Test 42 | void getDescription() { 43 | assertThat(this.testRule.getDescription()).isSameAs(PedanticEnforcerRule.POM_SECTION_ORDER); 44 | } 45 | 46 | @Override 47 | @Test 48 | void accept() { 49 | PedanticEnforcerVisitor visitor = mock(PedanticEnforcerVisitor.class); 50 | this.testRule.accept(visitor); 51 | 52 | verify(visitor).visit(this.testRule); 53 | } 54 | 55 | @Test 56 | void defaultSettingsCorrect() { 57 | configurePom(Arrays.asList(PomSection.values())); 58 | 59 | executeRuleAndCheckReport(false); 60 | } 61 | 62 | @Test 63 | void defaultSettingsWrongOrder() { 64 | // Put and in the wrong order. 65 | List sections = Arrays.asList(PomSection.values()); 66 | swapSections(sections, PomSection.DEPENDENCY_MANAGEMENT, PomSection.DEPENDENCIES); 67 | configurePom(sections); 68 | 69 | executeRuleAndCheckReport(true); 70 | } 71 | 72 | @Test 73 | void customSettingsCorrect() { 74 | // Parent declaration after project coordinates 75 | this.testRule.setSectionPriorities("modelVersion,groupId,artifactId,version,packaging,name,description,url,parent"); 76 | 77 | // Re-arrange the POM sections according to the enforcer configuration. 78 | List sections = new ArrayList<>(Arrays.asList(PomSection.values())); 79 | List reorderedSections = Arrays.asList( 80 | PomSection.MODEL_VERSION, PomSection.GROUP_ID, PomSection.ARTIFACT_ID, PomSection.VERSION, PomSection.PACKAGING, 81 | PomSection.NAME, PomSection.DESCRIPTION, PomSection.URL, PomSection.PARENT); 82 | 83 | sections.removeAll(reorderedSections); 84 | sections.addAll(0, reorderedSections); 85 | 86 | configurePom(sections); 87 | 88 | executeRuleAndCheckReport(false); 89 | } 90 | 91 | 92 | @Test 93 | void customSettingsWrongOrder() { 94 | this.testRule.setSectionPriorities("modelVersion,groupId,artifactId,version,packaging,name,description,url,parent"); 95 | 96 | configurePom(Arrays.asList(PomSection.values())); 97 | 98 | executeRuleAndCheckReport(true); 99 | } 100 | 101 | private void configurePom(Collection sections) { 102 | Document pom = this.testRule.getPom(); 103 | Element root = pom.getDocumentElement(); 104 | 105 | for (PomSection section : sections) { 106 | root.appendChild(pom.createElement(section.getSectionName())); 107 | } 108 | } 109 | 110 | private void swapSections(List sections, PomSection first, PomSection second) { 111 | int iFirst = sections.indexOf(first); 112 | int iSecond = sections.indexOf(second); 113 | 114 | sections.set(iFirst, second); 115 | sections.set(iSecond, first); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/PedanticModuleOrderEnforcer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collection; 20 | import java.util.List; 21 | import java.util.Set; 22 | import javax.inject.Inject; 23 | import javax.inject.Named; 24 | import org.apache.maven.project.MavenProject; 25 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 26 | import com.github.ferstl.maven.pomenforcers.util.CommaSeparatorUtils; 27 | import com.google.common.collect.Ordering; 28 | import com.google.common.collect.Sets; 29 | import static com.github.ferstl.maven.pomenforcers.ErrorReport.toList; 30 | 31 | 32 | /** 33 | * This enforcer makes sure that your modules section is sorted 34 | * alphabetically. Modules that should occur at a specific position in the 35 | * <modules> section can be ignored. 36 | *
 37 |  * ### Example
 38 |  *     <rules>
 39 |  *       <moduleOrder implementation="com.github.ferstl.maven.pomenforcers.PedanticModuleOrderEnforcer">
 40 |  *         <!-- These modules may occur at any place in the modules section -->
 41 |  *         <ignoredModules>dist-deb,dist-rpm</ignoredModules>
 42 |  *        </moduleOrder>
 43 |  *     </rules>
 44 |  * 
45 | * 46 | * @id {@link PedanticEnforcerRule#MODULE_ORDER} 47 | * @since 1.0.0 48 | */ 49 | @Named("moduleOrder") 50 | public class PedanticModuleOrderEnforcer extends AbstractPedanticEnforcer { 51 | 52 | /** 53 | * All modules in this set won't be checked for the correct order. 54 | */ 55 | private final Set ignoredModules; 56 | 57 | @Inject 58 | public PedanticModuleOrderEnforcer(final MavenProject project, final ExpressionEvaluator helper) { 59 | super(project, helper); 60 | this.ignoredModules = Sets.newLinkedHashSet(); 61 | } 62 | 63 | /** 64 | * Comma-separated list of ignored modules. All modules in this list may occur at any place in the 65 | * modules section. 66 | * 67 | * @param ignoredModules Comma-separated list of ignored modules. 68 | * @configParam 69 | * @since 1.0.0 70 | */ 71 | public void setIgnoredModules(String ignoredModules) { 72 | CommaSeparatorUtils.splitAndAddToCollection(ignoredModules, this.ignoredModules); 73 | } 74 | 75 | @Override 76 | protected PedanticEnforcerRule getDescription() { 77 | return PedanticEnforcerRule.MODULE_ORDER; 78 | } 79 | 80 | @Override 81 | protected void accept(PedanticEnforcerVisitor visitor) { 82 | visitor.visit(this); 83 | } 84 | 85 | @Override 86 | protected void doEnforce(ErrorReport report) { 87 | MavenProject project = getMavenProject(); 88 | // Do nothing if the project is not a parent project 89 | if (!isPomProject(project)) { 90 | return; 91 | } 92 | 93 | // Remove all modules to be ignored. 94 | List declaredModules = new ArrayList<>(getProjectModel().getModules()); 95 | declaredModules.removeAll(this.ignoredModules); 96 | 97 | // Enforce the module order 98 | Ordering moduleOrdering = Ordering.natural(); 99 | if (!moduleOrdering.isOrdered(declaredModules)) { 100 | reportError(report, declaredModules, moduleOrdering.immutableSortedCopy(declaredModules)); 101 | } 102 | } 103 | 104 | private boolean isPomProject(MavenProject project) { 105 | return "pom".equals(project.getPackaging()); 106 | } 107 | 108 | private void reportError(ErrorReport report, Collection declaredModules, Collection orderedModules) { 109 | report.addLine("You have to sort your modules alphabetically:") 110 | .emptyLine() 111 | .addDiff(declaredModules, orderedModules, "Actual Order", "Required Order"); 112 | if (!this.ignoredModules.isEmpty()) { 113 | report.emptyLine() 114 | .addLine("You may place these modules anywhere in your section:") 115 | .addLine(toList(this.ignoredModules)); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/util/XmlUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.util; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | import javax.xml.namespace.QName; 21 | import javax.xml.parsers.DocumentBuilder; 22 | import javax.xml.parsers.DocumentBuilderFactory; 23 | import javax.xml.parsers.ParserConfigurationException; 24 | import javax.xml.xpath.XPath; 25 | import javax.xml.xpath.XPathConstants; 26 | import javax.xml.xpath.XPathExpression; 27 | import javax.xml.xpath.XPathExpressionException; 28 | import javax.xml.xpath.XPathFactory; 29 | import org.w3c.dom.Document; 30 | import org.w3c.dom.Element; 31 | import org.w3c.dom.Node; 32 | import org.w3c.dom.NodeList; 33 | import org.xml.sax.SAXException; 34 | 35 | public final class XmlUtils { 36 | 37 | /** 38 | * Parses the given file into an XML {@link Document}. 39 | * 40 | * @param file The file to parse. 41 | * @return The created XML {@link Document}. 42 | */ 43 | public static Document parseXml(File file) { 44 | if (!file.exists()) { 45 | throw new IllegalArgumentException("File " + file + " does not exist."); 46 | } 47 | try { 48 | DocumentBuilder docBuilder = createDocumentBuilder(); 49 | return docBuilder.parse(file); 50 | } catch (SAXException | IOException e) { 51 | throw new IllegalStateException("Unable to parse XML file " + file, e); 52 | } 53 | } 54 | 55 | /** 56 | * Returns the XML {@link Element} matching the given XPath expression. 57 | * 58 | * @param expression XPath expression. 59 | * @param document XML document to search for the element. 60 | * @return The matching XML {@link Element}. 61 | */ 62 | public static Element evaluateXPathAsElement(String expression, Document document) { 63 | return evaluateXpath(expression, document, XPathConstants.NODE); 64 | } 65 | 66 | /** 67 | * Returns the XML {@link NodeList} matching the given XPath expression. 68 | * 69 | * @param expression XPath expression. 70 | * @param document XML document to search for the node list. 71 | * @return The matching XML {@link NodeList}. 72 | */ 73 | public static NodeList evaluateXPathAsNodeList(String expression, Document document) { 74 | return evaluateXpath(expression, document, XPathConstants.NODESET); 75 | } 76 | 77 | /** 78 | * Creates a XML document with the given root element and the given {@link NodeList} as content. 79 | * 80 | * @param root The root element of the XML document. 81 | * @param content Content of the XML document. 82 | * @return The created XML document. 83 | */ 84 | public static Document createDocument(String root, NodeList content) { 85 | DocumentBuilder docBuilder = createDocumentBuilder(); 86 | Document document = docBuilder.newDocument(); 87 | Element rootElement = document.createElement(root); 88 | document.appendChild(rootElement); 89 | 90 | for (int i = 0; i < content.getLength(); i++) { 91 | Node item = content.item(i); 92 | item = document.adoptNode(item.cloneNode(true)); 93 | rootElement.appendChild(item); 94 | } 95 | return document; 96 | } 97 | 98 | @SuppressWarnings("unchecked") 99 | private static T evaluateXpath(String expression, Document document, QName dataType) { 100 | try { 101 | XPath xpath = createXPath(); 102 | XPathExpression compiledExpression = xpath.compile(expression); 103 | return (T) compiledExpression.evaluate(document, dataType); 104 | } catch (XPathExpressionException e) { 105 | throw new IllegalArgumentException("Cannot evaluate XPath expression '" + expression + "'"); 106 | } 107 | } 108 | 109 | private static XPath createXPath() { 110 | return XPathFactory.newInstance().newXPath(); 111 | } 112 | 113 | private static DocumentBuilder createDocumentBuilder() { 114 | try { 115 | return DocumentBuilderFactory.newInstance().newDocumentBuilder(); 116 | } catch (ParserConfigurationException e) { 117 | throw new IllegalStateException("Cannot create document builder", e); 118 | } 119 | } 120 | 121 | private XmlUtils() { 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/PedanticPomSectionOrderEnforcer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collection; 20 | import java.util.List; 21 | import java.util.Set; 22 | import java.util.function.Function; 23 | import javax.inject.Inject; 24 | import javax.inject.Named; 25 | import org.apache.maven.project.MavenProject; 26 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 27 | import org.w3c.dom.Node; 28 | import org.w3c.dom.NodeList; 29 | import com.github.ferstl.maven.pomenforcers.model.PomSection; 30 | import com.github.ferstl.maven.pomenforcers.priority.PriorityOrdering; 31 | import com.github.ferstl.maven.pomenforcers.util.CommaSeparatorUtils; 32 | import com.google.common.collect.Ordering; 33 | import com.google.common.collect.Sets; 34 | 35 | 36 | /** 37 | * This enforcer makes sure that the sections in your POM files are in a defined order. 38 | *
 39 |  * ### Example
 40 |  *     <rules>
 41 |  *       <pomSectionOrder implementation="com.github.ferstl.maven.pomenforcers.PedanticPomSectionOrderEnforcer">
 42 |  *         <!-- Use project coordinates before parent declaration -->
 43 |  *         <sectionPriorities>groupId,artifactId,version,packaging</sectionPriorities>
 44 |  *       </pomSectionOrder>
 45 |  *     </rules>
 46 |  * 
47 | * 48 | * @id {@link PedanticEnforcerRule#POM_SECTION_ORDER} 49 | * @since 1.0.0 50 | */ 51 | @Named("pomSection") 52 | public class PedanticPomSectionOrderEnforcer extends AbstractPedanticEnforcer { 53 | 54 | private final Set sectionPriorities; 55 | 56 | @Inject 57 | public PedanticPomSectionOrderEnforcer(final MavenProject project, final ExpressionEvaluator helper) { 58 | super(project, helper); 59 | this.sectionPriorities = Sets.newLinkedHashSet(); 60 | } 61 | 62 | /** 63 | * Comma separated list of section priorities. 64 | * 65 | * @param sectionPriorities Comma separated list of section priorities. 66 | * @configParam 67 | * @default modelVersion, prerequisites, parent, groupId, artifactId, version 68 | * ,packaging,name,description,url,licenses,organization 69 | * ,inceptionYear,ciManagement,mailingLists,issueManagement, 70 | * developers ,contributors,scm,repositories,pluginRepositories 71 | * ,distributionManagement ,modules,properties,dependencyManagement 72 | * ,dependencies,build,profiles,reporting,reports 73 | * @since 1.0.0 74 | */ 75 | public void setSectionPriorities(String sectionPriorities) { 76 | CommaSeparatorUtils.splitAndAddToCollection(sectionPriorities, this.sectionPriorities, PomSection::getBySectionName); 77 | } 78 | 79 | @Override 80 | protected PedanticEnforcerRule getDescription() { 81 | return PedanticEnforcerRule.POM_SECTION_ORDER; 82 | } 83 | 84 | @Override 85 | protected void accept(PedanticEnforcerVisitor visitor) { 86 | visitor.visit(this); 87 | } 88 | 89 | @Override 90 | protected void doEnforce(ErrorReport report) { 91 | Node docElement = getPom().getDocumentElement(); 92 | NodeList sectionNodes = docElement.getChildNodes(); 93 | ArrayList pomSections = new ArrayList<>(); 94 | for (int i = 0; i < sectionNodes.getLength(); i++) { 95 | Node node = sectionNodes.item(i); 96 | if (node.getNodeType() == Node.ELEMENT_NODE) { 97 | pomSections.add(PomSection.getBySectionName(node.getNodeName())); 98 | } 99 | } 100 | 101 | // The default ordering is the order of the PomSection enum. 102 | Ordering ordering = createPriorityOrdering(this.sectionPriorities); 103 | 104 | if (!ordering.isOrdered(pomSections)) { 105 | List sortedPomSections = ordering.immutableSortedCopy(pomSections); 106 | 107 | report.addLine("Your POM has to be organized this way:") 108 | .emptyLine() 109 | .addDiff(pomSections, sortedPomSections, "Actual Order", "Required Order", PomSection::getSectionName); 110 | } 111 | } 112 | 113 | private static Ordering createPriorityOrdering(Collection priorityCollection) { 114 | return new PriorityOrdering<>(priorityCollection, Function.identity()); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/model/ArtifactModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers.model; 17 | 18 | import java.util.Objects; 19 | import javax.xml.bind.annotation.XmlElement; 20 | import com.google.common.base.Joiner; 21 | import static com.google.common.base.Objects.equal; 22 | 23 | public class ArtifactModel { 24 | 25 | private static final Joiner TO_STRING_JOINER = Joiner.on(":").useForNull(""); 26 | private static final String WILDCARD = "*"; 27 | private static final char WILDCARD_CHAR = WILDCARD.charAt(0); 28 | 29 | @XmlElement(namespace = "http://maven.apache.org/POM/4.0.0") 30 | private String groupId; 31 | @XmlElement(namespace = "http://maven.apache.org/POM/4.0.0") 32 | private String artifactId; 33 | @XmlElement(namespace = "http://maven.apache.org/POM/4.0.0") 34 | private String version; 35 | 36 | ArtifactModel() { 37 | } 38 | 39 | public ArtifactModel(String groupId, String artifactId, String version) { 40 | // Make sure that wildcards are valid 41 | determineWildcardMode(groupId); 42 | determineWildcardMode(artifactId); 43 | 44 | this.groupId = groupId; 45 | this.artifactId = artifactId; 46 | this.version = version; 47 | 48 | } 49 | 50 | public ArtifactModel(String groupId, String artifactId) { 51 | this(groupId, artifactId, null); 52 | } 53 | 54 | public String getGroupId() { 55 | return this.groupId; 56 | } 57 | 58 | public String getArtifactId() { 59 | return this.artifactId; 60 | } 61 | 62 | public String getVersion() { 63 | return this.version; 64 | } 65 | 66 | public boolean matches(ArtifactModel pattern) { 67 | if (pattern == this) { 68 | return true; 69 | } 70 | 71 | if (pattern == null) { 72 | return false; 73 | } 74 | 75 | return match(this.groupId, pattern.groupId) 76 | && match(this.artifactId, pattern.artifactId); 77 | } 78 | 79 | @Override 80 | public String toString() { 81 | return TO_STRING_JOINER.join( 82 | this.groupId, 83 | this.artifactId, 84 | this.version); 85 | } 86 | 87 | @Override 88 | public boolean equals(Object obj) { 89 | if (this == obj) { 90 | return true; 91 | } 92 | if (!(obj instanceof ArtifactModel)) { 93 | return false; 94 | } 95 | 96 | ArtifactModel other = (ArtifactModel) obj; 97 | return equal(this.groupId, other.groupId) 98 | && equal(this.artifactId, other.artifactId); 99 | } 100 | 101 | @Override 102 | public int hashCode() { 103 | return Objects.hash(this.groupId, this.artifactId); 104 | } 105 | 106 | private static WildcardMode determineWildcardMode(String string) { 107 | if (string == null) { 108 | return WildcardMode.NONE; 109 | } 110 | 111 | int wildcardCount = 0; 112 | for (int i = 0; i < string.length(); i++) { 113 | if (string.charAt(i) == WILDCARD_CHAR) { 114 | wildcardCount++; 115 | } 116 | } 117 | 118 | if (wildcardCount == 0) { 119 | return WildcardMode.NONE; 120 | } else if (wildcardCount == 1 && string.length() == 1) { 121 | return WildcardMode.FULL; 122 | } else if (wildcardCount == 1 && string.startsWith(WILDCARD)) { 123 | return WildcardMode.LEADING; 124 | } else if (wildcardCount == 1 && string.endsWith(WILDCARD)) { 125 | return WildcardMode.TRAILING; 126 | } else if (wildcardCount == 2 && string.startsWith(WILDCARD) && string.endsWith(WILDCARD)) { 127 | return WildcardMode.CONTAINS; 128 | } else { 129 | throw new IllegalArgumentException("Invalid wildcard pattern '" + string + "'"); 130 | } 131 | } 132 | 133 | private static boolean match(String string, String pattern) { 134 | switch (determineWildcardMode(pattern)) { 135 | case NONE: 136 | return string.equals(pattern); 137 | case LEADING: 138 | return string.endsWith(pattern.substring(1)); 139 | case TRAILING: 140 | return string.startsWith(pattern.substring(0, pattern.length() - 1)); 141 | case CONTAINS: 142 | return string.contains(pattern.substring(1, pattern.length() - 1)); 143 | case FULL: 144 | return true; 145 | default: 146 | return false; 147 | } 148 | } 149 | 150 | private enum WildcardMode { 151 | NONE, LEADING, TRAILING, CONTAINS, FULL 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/PedanticPluginManagementLocationEnforcer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import java.util.Collections; 19 | import java.util.HashSet; 20 | import java.util.Set; 21 | import javax.inject.Inject; 22 | import javax.inject.Named; 23 | import org.apache.maven.project.MavenProject; 24 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 25 | import com.github.ferstl.maven.pomenforcers.model.ArtifactModel; 26 | import com.github.ferstl.maven.pomenforcers.model.functions.StringToArtifactTransformer; 27 | import com.github.ferstl.maven.pomenforcers.util.CommaSeparatorUtils; 28 | import static com.github.ferstl.maven.pomenforcers.ErrorReport.toList; 29 | 30 | 31 | /** 32 | * Enforces that only a well-defined set of POMs may declare plugin management. 33 | *
 34 |  * ### Example
 35 |  *     <rules>
 36 |  *       <pluginManagemenLocation implementation="com.github.ferstl.maven.pomenforcers.PedanticPluginManagementLocationEnforcer">
 37 |  *         <!-- Only these POMs may declare plugin management -->
 38 |  *         <pluginManagingPoms>com.example.myproject:parent,com.example.myproject:subparent</pluginManagingPoms>
 39 |  *       </pluginManagemenLocation>
 40 |  *     </rules>
 41 |  * 
42 | * 43 | * @id {@link PedanticEnforcerRule#PLUGIN_MANAGEMENT_LOCATION} 44 | * @since 1.0.0 45 | */ 46 | @Named("pluginManagemenLocation") 47 | public class PedanticPluginManagementLocationEnforcer extends AbstractPedanticEnforcer { 48 | 49 | private boolean allowParentPoms; 50 | private final Set pluginManagingPoms; 51 | 52 | @Inject 53 | public PedanticPluginManagementLocationEnforcer(final MavenProject project, final ExpressionEvaluator helper) { 54 | super(project, helper); 55 | this.allowParentPoms = false; 56 | this.pluginManagingPoms = new HashSet<>(); 57 | } 58 | 59 | @Override 60 | protected PedanticEnforcerRule getDescription() { 61 | return PedanticEnforcerRule.PLUGIN_MANAGEMENT_LOCATION; 62 | } 63 | 64 | @Override 65 | protected void accept(PedanticEnforcerVisitor visitor) { 66 | visitor.visit(this); 67 | } 68 | 69 | @Override 70 | protected void doEnforce(ErrorReport report) { 71 | MavenProject mavenProject = getMavenProject(); 72 | if (containsPluginManagement() && !isPluginManagementAllowed(mavenProject)) { 73 | report.addLine("Only these POMs are allowed to manage plugins:") 74 | .addLine(toList(Collections.singletonList("All parent POMs, i.e. POMs with pom"))) 75 | .addLine(toList(this.pluginManagingPoms)); 76 | } 77 | } 78 | 79 | /** 80 | * Indicates whether parent POMs are generally allowed to manage plugins. 81 | * 82 | * @param allowParentPoms 83 | * @configParam 84 | * @default false 85 | * @since 1.2.0 86 | */ 87 | public void setAllowParentPoms(boolean allowParentPoms) { 88 | this.allowParentPoms = allowParentPoms; 89 | } 90 | 91 | /** 92 | * Comma separated list of POMs that may declare <pluginManagement>. Each POM has 93 | * to be defined in the format groupId:artifactId. 94 | * 95 | * @param pluginManagingPoms Comma separated list of POMs that may declare plugin management. 96 | * @configParam 97 | * @default n/a 98 | * @since 1.0.0 99 | */ 100 | public void setPluginManagingPoms(String pluginManagingPoms) { 101 | CommaSeparatorUtils.splitAndAddToCollection(pluginManagingPoms, this.pluginManagingPoms, StringToArtifactTransformer::toArtifactModel); 102 | } 103 | 104 | private boolean containsPluginManagement() { 105 | return !getProjectModel().getManagedPlugins().isEmpty(); 106 | } 107 | 108 | private boolean isPluginManagementAllowed(MavenProject project) { 109 | return isPluginManagementAllowedInParentPom(project) 110 | || isPluginManagingProject(project); 111 | } 112 | 113 | private boolean isPluginManagementAllowedInParentPom(MavenProject project) { 114 | return this.allowParentPoms && "pom".equals(project.getPackaging()); 115 | } 116 | 117 | private boolean isPluginManagingProject(MavenProject project) { 118 | ArtifactModel projectInfo = new ArtifactModel(project.getGroupId(), project.getArtifactId(), project.getVersion()); 119 | return this.pluginManagingPoms.isEmpty() || this.pluginManagingPoms.contains(projectInfo); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/AbstractPedanticDependencyOrderEnforcerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import org.junit.jupiter.api.BeforeEach; 19 | import org.junit.jupiter.api.Test; 20 | import com.github.ferstl.maven.pomenforcers.model.DependencyScope; 21 | 22 | 23 | /** 24 | *

25 | * Abstract test for {@link PedanticDependencyOrderEnforcer} and 26 | * {@link PedanticDependencyManagementOrderEnforcer}. Both classes do the same thing but one works 27 | * on the managed dependencies and one on the dependencies themselves. 28 | *

29 | */ 30 | abstract class AbstractPedanticDependencyOrderEnforcerTest 31 | extends AbstractPedanticEnforcerTest { 32 | 33 | private DependencyAdder dependencyAdder; 34 | 35 | @BeforeEach 36 | void setupDependencyAdder() { 37 | this.dependencyAdder = createDependencyAdder(); 38 | } 39 | 40 | protected abstract DependencyAdder createDependencyAdder(); 41 | 42 | @Test 43 | void defaultSettingsCorrect() { 44 | this.dependencyAdder.addDependency("d.e.f", "a", DependencyScope.IMPORT); 45 | this.dependencyAdder.addDependency("d.e.f", "b", DependencyScope.IMPORT); 46 | 47 | this.dependencyAdder.addDependency("a.b.c", "a", DependencyScope.COMPILE); 48 | this.dependencyAdder.addDependency("a.b.c", "b", DependencyScope.COMPILE); 49 | 50 | this.dependencyAdder.addDependency("g.h.i", "a", DependencyScope.PROVIDED); 51 | this.dependencyAdder.addDependency("g.h.i", "b", DependencyScope.PROVIDED); 52 | 53 | this.dependencyAdder.addDependency("j.k.l", "a", DependencyScope.SYSTEM); 54 | this.dependencyAdder.addDependency("j.k.l", "b", DependencyScope.SYSTEM); 55 | 56 | this.dependencyAdder.addDependency("m.n.o", "a", DependencyScope.TEST); 57 | this.dependencyAdder.addDependency("m.n.o", "b", DependencyScope.TEST); 58 | 59 | executeRuleAndCheckReport(false); 60 | } 61 | 62 | @Test 63 | void defaultSettingsWrongScopeOrder() { 64 | // Test before compile 65 | this.dependencyAdder.addDependency("a.b.c", "a", DependencyScope.TEST); 66 | this.dependencyAdder.addDependency("x.y.z", "z", DependencyScope.COMPILE); 67 | 68 | executeRuleAndCheckReport(true); 69 | } 70 | 71 | @Test 72 | void defaultSettingsWrongGroupIdOrder() { 73 | this.dependencyAdder.addDependency("d.e.f", "a", DependencyScope.COMPILE); 74 | this.dependencyAdder.addDependency("a.b.c", "a", DependencyScope.COMPILE); 75 | 76 | executeRuleAndCheckReport(true); 77 | } 78 | 79 | @Test 80 | void defaultSettingsWrongArtifactIdOrder() { 81 | this.dependencyAdder.addDependency("a.b.c", "b", DependencyScope.COMPILE); 82 | this.dependencyAdder.addDependency("a.b.c", "a", DependencyScope.COMPILE); 83 | 84 | executeRuleAndCheckReport(true); 85 | } 86 | 87 | @Test 88 | void groupIdPriorities() { 89 | this.testRule.setGroupIdPriorities("u.v.w,x.y.z"); 90 | 91 | this.dependencyAdder.addDependency("u.v.w", "z", DependencyScope.COMPILE); 92 | this.dependencyAdder.addDependency("x.y.z", "z", DependencyScope.COMPILE); 93 | this.dependencyAdder.addDependency("a.b.c", "a", DependencyScope.COMPILE); 94 | 95 | executeRuleAndCheckReport(false); 96 | } 97 | 98 | 99 | @Test 100 | void artifactIdPriorities() { 101 | this.testRule.setArtifactIdPriorities("z,y"); 102 | 103 | this.dependencyAdder.addDependency("a.b.c", "z", DependencyScope.COMPILE); 104 | this.dependencyAdder.addDependency("a.b.c", "y", DependencyScope.COMPILE); 105 | this.dependencyAdder.addDependency("a.b.c", "a", DependencyScope.COMPILE); 106 | 107 | executeRuleAndCheckReport(false); 108 | } 109 | 110 | @Test 111 | void scopePriorities() { 112 | this.testRule.setScopePriorities("system,compile"); 113 | 114 | this.dependencyAdder.addDependency("x.y.z", "z", DependencyScope.SYSTEM); 115 | this.dependencyAdder.addDependency("a.b.c", "a", DependencyScope.COMPILE); 116 | 117 | executeRuleAndCheckReport(false); 118 | } 119 | 120 | @Test 121 | void orderBy() { 122 | this.testRule.setOrderBy("groupId,artifactId"); 123 | 124 | this.dependencyAdder.addDependency("a.b.c", "a", DependencyScope.TEST); 125 | this.dependencyAdder.addDependency("a.b.c", "b", DependencyScope.COMPILE); 126 | 127 | executeRuleAndCheckReport(false); 128 | } 129 | 130 | @FunctionalInterface 131 | interface DependencyAdder { 132 | 133 | void addDependency(String groupId, String artifactId, DependencyScope scope); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/PedanticDependencyManagementLocationEnforcer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import java.util.Collections; 19 | import java.util.HashSet; 20 | import java.util.Set; 21 | import javax.inject.Inject; 22 | import javax.inject.Named; 23 | import org.apache.maven.project.MavenProject; 24 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 25 | import com.github.ferstl.maven.pomenforcers.model.ArtifactModel; 26 | import com.github.ferstl.maven.pomenforcers.model.functions.StringToArtifactTransformer; 27 | import static com.github.ferstl.maven.pomenforcers.ErrorReport.toList; 28 | import static com.github.ferstl.maven.pomenforcers.util.CommaSeparatorUtils.splitAndAddToCollection; 29 | 30 | /** 31 | * Enforces that only a well-defined set of POMs may declare dependency management. 32 | *
 33 |  * ### Example
 34 |  *     <rules>
 35 |  *       <dependencyManagementLocation implementation="com.github.ferstl.maven.pomenforcers.PedanticDependencyManagementLocationEnforcer">
 36 |  *         <!-- Only these POMs may declare dependency management -->
 37 |  *         <dependencyManagingPoms>com.example.myproject:parent,com.example.myproject:subparent</dependencyManagingPoms>
 38 |  *       </dependencyManagementLocation>
 39 |  *     </rules>
 40 |  * 
41 | * 42 | * @id {@link PedanticEnforcerRule#DEPENDENCY_MANAGEMENT_LOCATION} 43 | * @since 1.0.0 44 | */ 45 | @Named("dependencyManagementLocation") 46 | public class PedanticDependencyManagementLocationEnforcer extends AbstractPedanticEnforcer { 47 | 48 | private boolean allowParentPoms; 49 | private final Set dependencyManagingPoms; 50 | 51 | @Inject 52 | public PedanticDependencyManagementLocationEnforcer(final MavenProject project, final ExpressionEvaluator helper) { 53 | super(project, helper); 54 | this.allowParentPoms = false; 55 | this.dependencyManagingPoms = new HashSet<>(); 56 | } 57 | 58 | /** 59 | * Indicates whether parent POMs are generally allowed to manage plugins. 60 | * 61 | * @param allowParentPoms 62 | * @configParam 63 | * @default false 64 | * @since 1.2.0 65 | */ 66 | public void setAllowParentPoms(boolean allowParentPoms) { 67 | this.allowParentPoms = allowParentPoms; 68 | } 69 | 70 | /** 71 | * Comma separated list of POMs that may declare <dependencyManagement>. 72 | * Each POM has to be defined in the format groupId:artifactId. 73 | * 74 | * @param dependencyManagingPoms Comma separated list of POMs that may declare plugin management. 75 | * @configParam 76 | * @default n/a 77 | * @since 1.0.0 78 | */ 79 | public void setDependencyManagingPoms(String dependencyManagingPoms) { 80 | splitAndAddToCollection(dependencyManagingPoms, this.dependencyManagingPoms, StringToArtifactTransformer::toArtifactModel); 81 | } 82 | 83 | @Override 84 | protected PedanticEnforcerRule getDescription() { 85 | return PedanticEnforcerRule.DEPENDENCY_MANAGEMENT_LOCATION; 86 | } 87 | 88 | @Override 89 | protected void accept(PedanticEnforcerVisitor visitor) { 90 | visitor.visit(this); 91 | } 92 | 93 | @Override 94 | protected void doEnforce(ErrorReport report) { 95 | MavenProject mavenProject = getMavenProject(); 96 | if (containsDependencyManagement() && !isDependencyManagementAllowed(mavenProject)) { 97 | report.addLine("Only these POMs are allowed to manage dependencies:") 98 | .addLine(toList(Collections.singletonList("All parent POMs, i.e. POMs with pom"))) 99 | .addLine(toList(this.dependencyManagingPoms)); 100 | } 101 | } 102 | 103 | private boolean containsDependencyManagement() { 104 | return !getProjectModel().getManagedDependencies().isEmpty(); 105 | } 106 | 107 | private boolean isDependencyManagementAllowed(MavenProject project) { 108 | return isDependencyManagementAllowedInParentPom(project) 109 | || isDependencyManagingProject(project); 110 | } 111 | 112 | private boolean isDependencyManagementAllowedInParentPom(MavenProject project) { 113 | return this.allowParentPoms && "pom".equals(project.getPackaging()); 114 | } 115 | 116 | private boolean isDependencyManagingProject(MavenProject project) { 117 | ArtifactModel projectInfo = new ArtifactModel(project.getGroupId(), project.getArtifactId(), project.getVersion()); 118 | return this.dependencyManagingPoms.isEmpty() || this.dependencyManagingPoms.contains(projectInfo); 119 | 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/PedanticDependencyScopeEnforcerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import java.lang.invoke.MethodHandle; 19 | import java.lang.invoke.MethodHandles; 20 | import java.lang.invoke.MethodType; 21 | import java.util.ArrayList; 22 | import java.util.Arrays; 23 | import java.util.List; 24 | import org.codehaus.plexus.util.StringUtils; 25 | import org.junit.jupiter.api.BeforeEach; 26 | import org.junit.jupiter.api.Test; 27 | import org.junit.jupiter.params.ParameterizedTest; 28 | import org.junit.jupiter.params.provider.MethodSource; 29 | import com.github.ferstl.maven.pomenforcers.model.DependencyScope; 30 | import com.google.common.base.Joiner; 31 | import com.google.common.collect.Collections2; 32 | import static org.assertj.core.api.Assertions.assertThat; 33 | import static org.mockito.Mockito.mock; 34 | import static org.mockito.Mockito.verify; 35 | 36 | /** 37 | * JUnit tests for {@link PedanticDependencyScopeEnforcer}. 38 | */ 39 | class PedanticDependencyScopeEnforcerTest extends AbstractPedanticEnforcerTest { 40 | 41 | /** 42 | * Creates test data for each possible dependency scope. The data will be used as theory to test the enforcer rule 43 | * with different settings. This prevents writing a test method for each dependency scope. 44 | */ 45 | private static RuleConfiguration[] ruleConfigurations() throws Exception { 46 | List ruleConfig = new ArrayList<>(2 * DependencyScope.values().length); 47 | 48 | for (DependencyScope scope : DependencyScope.values()) { 49 | String configMethodName = "set" + StringUtils.capitalise(scope.getScopeName()) + "Dependencies"; 50 | MethodType configMethodType = MethodType.methodType(void.class, String.class); 51 | MethodHandle configMethod = MethodHandles.publicLookup().findVirtual( 52 | PedanticDependencyScopeEnforcer.class, configMethodName, configMethodType); 53 | 54 | ruleConfig.add(new RuleConfiguration(configMethod, createCorrectConfiguration(scope), false)); 55 | ruleConfig.add(new RuleConfiguration(configMethod, createWrongConfiguration(scope), true)); 56 | } 57 | return ruleConfig.toArray(new RuleConfiguration[0]); 58 | } 59 | 60 | @Override 61 | PedanticDependencyScopeEnforcer createRule() { 62 | return new PedanticDependencyScopeEnforcer(this.mockMavenProject, this.mockHelper); 63 | } 64 | 65 | @BeforeEach 66 | void before() { 67 | addDependenciesForAllScopes(); 68 | } 69 | 70 | @Override 71 | @Test 72 | void getDescription() { 73 | assertThat(this.testRule.getDescription()).isEqualTo(PedanticEnforcerRule.DEPENDENCY_SCOPE); 74 | } 75 | 76 | @Override 77 | @Test 78 | void accept() { 79 | PedanticEnforcerVisitor visitor = mock(PedanticEnforcerVisitor.class); 80 | this.testRule.accept(visitor); 81 | 82 | verify(visitor).visit(this.testRule); 83 | } 84 | 85 | @Test 86 | void nothingConfigured() { 87 | executeRuleAndCheckReport(false); 88 | } 89 | 90 | @ParameterizedTest 91 | @MethodSource("ruleConfigurations") 92 | void allConfigurations(RuleConfiguration param) throws Throwable { 93 | param.configureRule(this.testRule); 94 | 95 | executeRuleAndCheckReport(param.resultHasErrors); 96 | } 97 | 98 | @Test 99 | void correctCompileDependencies() { 100 | this.testRule.setCompileDependencies("a.b.c:dep-compile"); 101 | 102 | executeRuleAndCheckReport(false); 103 | } 104 | 105 | @Test 106 | void wrongCompileDependencies() { 107 | this.testRule.setCompileDependencies("a.b.c:dep-test,a.b.c:dep-runtime"); 108 | this.testRule.setRuntimeDependencies("a.b.c:dep-test"); 109 | 110 | executeRuleAndCheckReport(true); 111 | } 112 | 113 | @Test 114 | void wildcardGroupId() { 115 | this.testRule.setCompileDependencies("a.b.c:*-runtime"); 116 | 117 | executeRuleAndCheckReport(true); 118 | } 119 | 120 | 121 | private void addDependenciesForAllScopes() { 122 | for (DependencyScope scope : DependencyScope.values()) { 123 | addDependency("a.b.c", "dep-" + scope.getScopeName(), scope); 124 | } 125 | } 126 | 127 | private static String createWrongConfiguration(DependencyScope scope) { 128 | ArrayList scopes = new ArrayList<>(Arrays.asList(DependencyScope.values())); 129 | scopes.remove(scope); 130 | 131 | return Joiner.on(",").join(Collections2.transform(scopes, PedanticDependencyScopeEnforcerTest::createDependencyString)); 132 | } 133 | 134 | private static String createCorrectConfiguration(DependencyScope scope) { 135 | return createDependencyString(scope); 136 | } 137 | 138 | private static String createDependencyString(DependencyScope scope) { 139 | return "a.b.c:dep-" + scope.getScopeName(); 140 | } 141 | 142 | 143 | static class RuleConfiguration { 144 | 145 | private final MethodHandle configMethod; 146 | private final String configArgument; 147 | private final boolean resultHasErrors; 148 | 149 | RuleConfiguration(MethodHandle configMethod, String configArgument, boolean resultHasErrors) { 150 | this.configMethod = configMethod; 151 | this.configArgument = configArgument; 152 | this.resultHasErrors = resultHasErrors; 153 | } 154 | 155 | void configureRule(PedanticDependencyScopeEnforcer rule) throws Throwable { 156 | this.configMethod.invoke(rule, this.configArgument); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/test/java/com/github/ferstl/maven/pomenforcers/AbstractPedanticEnforcerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import java.util.LinkedList; 19 | import javax.xml.parsers.DocumentBuilder; 20 | import javax.xml.parsers.DocumentBuilderFactory; 21 | import javax.xml.parsers.ParserConfigurationException; 22 | import org.apache.maven.model.Dependency; 23 | import org.apache.maven.model.DependencyManagement; 24 | import org.apache.maven.model.PluginManagement; 25 | import org.apache.maven.project.MavenProject; 26 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 27 | import org.junit.jupiter.api.BeforeEach; 28 | import org.junit.jupiter.api.Test; 29 | import org.w3c.dom.Document; 30 | import org.w3c.dom.Element; 31 | import com.github.ferstl.maven.pomenforcers.model.DependencyModel; 32 | import com.github.ferstl.maven.pomenforcers.model.DependencyScope; 33 | import com.github.ferstl.maven.pomenforcers.model.ProjectModel; 34 | import static com.github.ferstl.maven.pomenforcers.ErrorReportAssert.assertThat; 35 | import static org.mockito.Mockito.mock; 36 | import static org.mockito.Mockito.when; 37 | 38 | 39 | abstract class AbstractPedanticEnforcerTest { 40 | 41 | private static final String DEFAULT_VERSION = "1.0"; 42 | 43 | ProjectModel projectModel; 44 | MavenProject mockMavenProject; 45 | T testRule; 46 | ErrorReport report; 47 | 48 | ExpressionEvaluator mockHelper; 49 | 50 | @BeforeEach 51 | void setup() throws Exception { 52 | this.mockHelper = mock(ExpressionEvaluator.class); 53 | this.projectModel = mock(ProjectModel.class); 54 | this.mockMavenProject = mock(MavenProject.class); 55 | 56 | when(this.projectModel.getDependencies()).thenReturn(new LinkedList<>()); 57 | when(this.projectModel.getManagedDependencies()).thenReturn(new LinkedList<>()); 58 | when(this.projectModel.getPlugins()).thenReturn(new LinkedList<>()); 59 | when(this.projectModel.getManagedPlugins()).thenReturn(new LinkedList<>()); 60 | when(this.mockMavenProject.getDependencies()).thenReturn(new LinkedList<>()); 61 | DependencyManagement depMgmtMock = mock(DependencyManagement.class); 62 | PluginManagement pluginMgmtMock = mock(PluginManagement.class); 63 | when(depMgmtMock.getDependencies()).thenReturn(new LinkedList<>()); 64 | when(pluginMgmtMock.getPlugins()).thenReturn(new LinkedList<>()); 65 | when(this.mockMavenProject.getDependencyManagement()).thenReturn(depMgmtMock); 66 | when(this.mockMavenProject.getPluginManagement()).thenReturn(pluginMgmtMock); 67 | 68 | when(this.mockHelper.evaluate("${project}")).thenReturn(this.mockMavenProject); 69 | 70 | this.testRule = createRule(); 71 | this.testRule.initialize(createEmptyPom(), this.projectModel); 72 | this.report = new ErrorReport(this.testRule.getDescription()); 73 | } 74 | 75 | abstract T createRule(); 76 | 77 | @Test 78 | abstract void getDescription(); 79 | 80 | @Test 81 | abstract void accept(); 82 | 83 | protected void executeRuleAndCheckReport(boolean hasErrors) { 84 | this.testRule.doEnforce(this.report); 85 | 86 | if (hasErrors) { 87 | assertThat(this.report).hasErrors(); 88 | } else { 89 | assertThat(this.report).hasNoErrors(); 90 | } 91 | } 92 | 93 | protected void addDependency(String groupId, String artifactId, DependencyScope scope) { 94 | String version = DEFAULT_VERSION; 95 | 96 | Dependency mavenDependency = createMavenDependency(groupId, artifactId, scope, version); 97 | DependencyModel dependency = createDependencyModel(groupId, artifactId, scope, version); 98 | 99 | this.mockMavenProject.getDependencies().add(mavenDependency); 100 | this.projectModel.getDependencies().add(dependency); 101 | } 102 | 103 | protected void addManagedDependency(String groupId, String artifactId, DependencyScope scope) { 104 | String version = DEFAULT_VERSION; 105 | 106 | Dependency mavenDependency = createMavenDependency(groupId, artifactId, scope, version); 107 | DependencyModel dependency = createDependencyModel(groupId, artifactId, version); 108 | 109 | this.mockMavenProject.getDependencyManagement().getDependencies().add(mavenDependency); 110 | this.projectModel.getManagedDependencies().add(dependency); 111 | } 112 | 113 | private static DependencyModel createDependencyModel(String groupId, String artifactId, String version) { 114 | return createDependencyModel(groupId, artifactId, null, version); 115 | } 116 | 117 | private static DependencyModel createDependencyModel(String groupId, String artifactId, DependencyScope scope, String version) { 118 | return new DependencyModel(groupId, artifactId, version, scope != null ? scope.getScopeName() : null, null, null); 119 | } 120 | 121 | private static Dependency createMavenDependency(String groupId, String artifactId, DependencyScope scope, String version) { 122 | Dependency mavenDependency = new Dependency(); 123 | mavenDependency.setGroupId(groupId); 124 | mavenDependency.setArtifactId(artifactId); 125 | mavenDependency.setVersion(version); 126 | mavenDependency.setScope(scope.getScopeName()); 127 | return mavenDependency; 128 | } 129 | 130 | private static Document createEmptyPom() { 131 | DocumentBuilder docBuilder = createDocumentBuilder(); 132 | Document document = docBuilder.newDocument(); 133 | Element rootElement = document.createElement("project"); 134 | 135 | document.appendChild(rootElement); 136 | 137 | return document; 138 | } 139 | 140 | private static DocumentBuilder createDocumentBuilder() { 141 | try { 142 | return DocumentBuilderFactory.newInstance().newDocumentBuilder(); 143 | } catch (ParserConfigurationException e) { 144 | throw new IllegalStateException("Cannot create document builder", e); 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/AbstractPedanticDependencyOrderEnforcer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import java.util.Collection; 19 | import java.util.EnumSet; 20 | import java.util.LinkedHashSet; 21 | import java.util.Set; 22 | import org.apache.maven.model.Dependency; 23 | import org.apache.maven.project.MavenProject; 24 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 25 | import com.github.ferstl.maven.pomenforcers.model.DependencyElement; 26 | import com.github.ferstl.maven.pomenforcers.model.DependencyModel; 27 | import com.github.ferstl.maven.pomenforcers.model.DependencyScope; 28 | import com.github.ferstl.maven.pomenforcers.model.functions.DependencyMatcher; 29 | import com.github.ferstl.maven.pomenforcers.priority.CompoundPriorityOrdering; 30 | import com.github.ferstl.maven.pomenforcers.util.CommaSeparatorUtils; 31 | import com.google.common.collect.BiMap; 32 | import com.google.common.collect.Sets; 33 | import static com.github.ferstl.maven.pomenforcers.model.DependencyElement.ARTIFACT_ID; 34 | import static com.github.ferstl.maven.pomenforcers.model.DependencyElement.GROUP_ID; 35 | import static com.github.ferstl.maven.pomenforcers.model.DependencyElement.SCOPE; 36 | import static java.util.stream.Collectors.toList; 37 | 38 | 39 | abstract class AbstractPedanticDependencyOrderEnforcer extends AbstractPedanticEnforcer { 40 | 41 | private final CompoundPriorityOrdering artifactOrdering; 42 | 43 | public AbstractPedanticDependencyOrderEnforcer(final MavenProject project, final ExpressionEvaluator helper) { 44 | super(project, helper); 45 | this.artifactOrdering = CompoundPriorityOrdering.orderBy(SCOPE, GROUP_ID, ARTIFACT_ID); 46 | Iterable scopePriorities = EnumSet.allOf(DependencyScope.class) 47 | .stream() 48 | .map(DependencyScope::getScopeName) 49 | .collect(toList()); 50 | this.artifactOrdering.setPriorities(SCOPE, scopePriorities); 51 | } 52 | 53 | /** 54 | * Comma-separated list of dependency elements that defines the ordering. 55 | * 56 | * @param dependencyElements Comma-separated list of dependency elements that defines the ordering. 57 | * @configParam 58 | * @default scope, groupId, artifactId 59 | * @since 1.0.0 60 | */ 61 | public void setOrderBy(String dependencyElements) { 62 | Set orderBy = new LinkedHashSet<>(); 63 | CommaSeparatorUtils.splitAndAddToCollection(dependencyElements, orderBy, DependencyElement::getByElementName); 64 | this.artifactOrdering.redefineOrderBy(orderBy); 65 | } 66 | 67 | /** 68 | * Comma-separated list of group IDs that should be listed first in the 69 | * dependencies declaration. All group IDs that start with 70 | * any of the prioritized group IDs in the given list, are required to be 71 | * located first in the dependencies section. 72 | * 73 | * @param groupIds Comma separated list of group IDs. 74 | * @configParam 75 | * @default n/a 76 | * @since 1.0.0 77 | */ 78 | public void setGroupIdPriorities(String groupIds) { 79 | LinkedHashSet groupIdPriorities = Sets.newLinkedHashSet(); 80 | CommaSeparatorUtils.splitAndAddToCollection(groupIds, groupIdPriorities); 81 | this.artifactOrdering.setPriorities(DependencyElement.GROUP_ID, groupIdPriorities); 82 | } 83 | 84 | /** 85 | * Comma-separated list of artifact IDs that should be listed first in the 86 | * dependencies declaration. All artifact IDs that start with 87 | * any of the prioritized IDs in the given list, are required to be located 88 | * first in the dependencies section. 89 | * 90 | * @param artifactIds Comma separated list of artifact IDs. 91 | * @configParam 92 | * @default n/a 93 | * @since 1.0.0 94 | */ 95 | public void setArtifactIdPriorities(String artifactIds) { 96 | LinkedHashSet artifactIdPriorities = Sets.newLinkedHashSet(); 97 | CommaSeparatorUtils.splitAndAddToCollection(artifactIds, artifactIdPriorities); 98 | this.artifactOrdering.setPriorities(DependencyElement.ARTIFACT_ID, artifactIdPriorities); 99 | } 100 | 101 | /** 102 | * Comma-separated list of scopes that should be listed first in the 103 | * dependencies declaration. All scopes that equal any of the scopes in the 104 | * given list, are required to be located first in the dependencies section. 105 | * 106 | * @param scopes Comma separated list of scopes. 107 | * @configParam 108 | * @default import, compile, provided, runtime, system, test 109 | * @since 1.0.0 110 | */ 111 | public void setScopePriorities(String scopes) { 112 | LinkedHashSet scopePriorities = Sets.newLinkedHashSet(); 113 | CommaSeparatorUtils.splitAndAddToCollection(scopes, scopePriorities); 114 | this.artifactOrdering.setPriorities(DependencyElement.SCOPE, scopePriorities); 115 | } 116 | 117 | protected abstract Collection getDeclaredDependencies(); 118 | 119 | protected abstract Collection getMavenDependencies(MavenProject mavenProject); 120 | 121 | protected abstract void reportError( 122 | ErrorReport report, Collection resolvedDependencies, Collection sortedDependencies); 123 | 124 | @Override 125 | protected final void doEnforce(ErrorReport report) { 126 | MavenProject mavenProject = getMavenProject(); 127 | DependencyMatcher dependencyMatcher = new DependencyMatcher(getHelper()); 128 | 129 | BiMap matchedDependencies = 130 | dependencyMatcher.match(getMavenDependencies(mavenProject), getDeclaredDependencies()); 131 | 132 | Set resolvedDependencies = matchedDependencies.keySet(); 133 | if (!this.artifactOrdering.isOrdered(resolvedDependencies)) { 134 | reportError(report, resolvedDependencies, this.artifactOrdering.immutableSortedCopy(resolvedDependencies)); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/com/github/ferstl/maven/pomenforcers/PedanticEnforcerRule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 - 2025 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.ferstl.maven.pomenforcers; 17 | 18 | import org.apache.maven.project.MavenProject; 19 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 20 | 21 | /** 22 | * Each pedantic enforcer rule is identified by an ID. These IDs can be used within the 23 | * {@link CompoundPedanticEnforcer} to enable specific rules. The compound enforcer is more efficient 24 | * because it parses the POM file of each Maven module only once and delegates it to the configured 25 | * enforcer rules. 26 | */ 27 | public enum PedanticEnforcerRule { 28 | 29 | COMPOUND("One does not simply write a POM file!") { 30 | @Override 31 | public AbstractPedanticEnforcer createEnforcerRule(final MavenProject project, final ExpressionEvaluator helper) { 32 | throw new UnsupportedOperationException( 33 | "The " + CompoundPedanticEnforcer.class.getSimpleName() 34 | + " is not supposed to be instantiated outside the maven-enforcer-plugin."); 35 | } 36 | }, 37 | 38 | /** 39 | * @see PedanticPomSectionOrderEnforcer 40 | */ 41 | POM_SECTION_ORDER("One does not simply write a POM file!") { 42 | @Override 43 | public AbstractPedanticEnforcer createEnforcerRule(final MavenProject project, final ExpressionEvaluator helper) { 44 | return new PedanticPomSectionOrderEnforcer(project, helper); 45 | } 46 | }, 47 | /** 48 | * @see PedanticModuleOrderEnforcer 49 | */ 50 | MODULE_ORDER("One does not simply declare modules!") { 51 | @Override 52 | public AbstractPedanticEnforcer createEnforcerRule(final MavenProject project, final ExpressionEvaluator helper) { 53 | return new PedanticModuleOrderEnforcer(project, helper); 54 | } 55 | }, 56 | /** 57 | * @see PedanticDependencyManagementOrderEnforcer 58 | */ 59 | DEPENDENCY_MANAGEMENT_ORDER("One does not simply declare dependency management!") { 60 | @Override 61 | public AbstractPedanticEnforcer createEnforcerRule(final MavenProject project, final ExpressionEvaluator helper) { 62 | return new PedanticDependencyManagementOrderEnforcer(project, helper); 63 | } 64 | }, 65 | /** 66 | * @see PedanticDependencyManagementLocationEnforcer 67 | */ 68 | DEPENDENCY_MANAGEMENT_LOCATION("One does not simply declare dependency management!") { 69 | @Override 70 | public AbstractPedanticEnforcer createEnforcerRule(final MavenProject project, final ExpressionEvaluator helper) { 71 | return new PedanticDependencyManagementLocationEnforcer(project, helper); 72 | } 73 | }, 74 | /** 75 | * @see PedanticDependencyOrderEnforcer 76 | */ 77 | DEPENDENCY_ORDER("One does not simply declare dependencies!") { 78 | @Override 79 | public AbstractPedanticEnforcer createEnforcerRule(final MavenProject project, final ExpressionEvaluator helper) { 80 | return new PedanticDependencyOrderEnforcer(project, helper); 81 | } 82 | }, 83 | /** 84 | * @see PedanticDependencyConfigurationEnforcer 85 | */ 86 | DEPENDENCY_CONFIGURATION("One does not simply configure dependencies!") { 87 | @Override 88 | public AbstractPedanticEnforcer createEnforcerRule(final MavenProject project, final ExpressionEvaluator helper) { 89 | return new PedanticDependencyConfigurationEnforcer(project, helper); 90 | } 91 | }, 92 | 93 | /** 94 | * @see PedanticDependencyElementEnforcer 95 | */ 96 | DEPENDENCY_ELEMENT("One does not simply declare a dependency!") { 97 | @Override 98 | public AbstractPedanticEnforcer createEnforcerRule(final MavenProject project, final ExpressionEvaluator helper) { 99 | return new PedanticDependencyElementEnforcer(project, helper); 100 | } 101 | }, 102 | 103 | /** 104 | * @see PedanticDependencyScopeEnforcer 105 | */ 106 | DEPENDENCY_SCOPE("One does not simply declare dependency scopes!") { 107 | @Override 108 | public AbstractPedanticEnforcer createEnforcerRule(final MavenProject project, final ExpressionEvaluator helper) { 109 | return new PedanticDependencyScopeEnforcer(project, helper); 110 | } 111 | }, 112 | /** 113 | * @see PedanticPluginManagementOrderEnforcer 114 | */ 115 | PLUGIN_MANAGEMENT_ORDER("One does not simply declare plugin management!") { 116 | @Override 117 | public AbstractPedanticEnforcer createEnforcerRule(final MavenProject project, final ExpressionEvaluator helper) { 118 | return new PedanticPluginManagementOrderEnforcer(project, helper); 119 | } 120 | }, 121 | /** 122 | * @see PedanticPluginConfigurationEnforcer 123 | */ 124 | PLUGIN_CONFIGURATION("One does not simply configure plugins!") { 125 | @Override 126 | public AbstractPedanticEnforcer createEnforcerRule(final MavenProject project, final ExpressionEvaluator helper) { 127 | return new PedanticPluginConfigurationEnforcer(project, helper); 128 | } 129 | }, 130 | 131 | /** 132 | * @see PedanticPluginElementEnforcer 133 | */ 134 | PLUGIN_ELEMENT("One does not simple declare a plugin!") { 135 | @Override 136 | public AbstractPedanticEnforcer createEnforcerRule(final MavenProject project, final ExpressionEvaluator helper) { 137 | return new PedanticPluginElementEnforcer(project, helper); 138 | } 139 | }, 140 | /** 141 | * @see PedanticPluginManagementLocationEnforcer 142 | */ 143 | PLUGIN_MANAGEMENT_LOCATION("One does not simply declare plugin management!") { 144 | @Override 145 | public AbstractPedanticEnforcer createEnforcerRule(final MavenProject project, final ExpressionEvaluator helper) { 146 | return new PedanticPluginManagementLocationEnforcer(project, helper); 147 | } 148 | }; 149 | 150 | private final String slogan; 151 | 152 | PedanticEnforcerRule(String slogan) { 153 | this.slogan = slogan; 154 | } 155 | 156 | public String getSlogan() { 157 | return this.slogan; 158 | } 159 | 160 | public abstract AbstractPedanticEnforcer createEnforcerRule(final MavenProject project, final ExpressionEvaluator helper); 161 | } 162 | --------------------------------------------------------------------------------