diff --git a/.github/workflows/promote.yml b/.github/workflows/promote.yml index cad6e40b..e1faac58 100644 --- a/.github/workflows/promote.yml +++ b/.github/workflows/promote.yml @@ -36,7 +36,7 @@ jobs: - name: Check Out uses: actions/checkout@v4 - name: Set Up JFrog CLI - uses: jfrog/setup-jfrog-cli@7c95feb32008765e1b4e626b078dfd897c4340ad # v4.1.2 + uses: jfrog/setup-jfrog-cli@ff5cb544114ffc152db9cea1cd3d5978d5074946 # v4.5.11 env: JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }} - name: Check Maven Central Sync Status diff --git a/README.adoc b/README.adoc index cb549f97..2c00258a 100644 --- a/README.adoc +++ b/README.adoc @@ -1,4 +1,4 @@ -:release-version: 0.0.43 +:release-version: 0.0.45 :checkstyle-version: 9.3 == Spring Java Format diff --git a/pom.xml b/pom.xml index c7e0ba80..5058125d 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 io.spring.javaformat spring-javaformat-build - 0.0.44-SNAPSHOT + 0.0.46 pom Spring JavaFormat Build Spring JavaFormat diff --git a/samples/spring-javaformat-gradle-sample/build.gradle b/samples/spring-javaformat-gradle-sample/build.gradle index 251a0478..cf53cc79 100644 --- a/samples/spring-javaformat-gradle-sample/build.gradle +++ b/samples/spring-javaformat-gradle-sample/build.gradle @@ -4,7 +4,7 @@ buildscript { mavenCentral() } dependencies { - classpath("io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.44-SNAPSHOT") + classpath("io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.46-SNAPSHOT") } } @@ -25,5 +25,5 @@ checkstyle { } dependencies { - checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.44-SNAPSHOT") + checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.46-SNAPSHOT") } diff --git a/samples/spring-javaformat-maven-sample/pom.xml b/samples/spring-javaformat-maven-sample/pom.xml index cf225184..22ac6ca4 100644 --- a/samples/spring-javaformat-maven-sample/pom.xml +++ b/samples/spring-javaformat-maven-sample/pom.xml @@ -8,7 +8,7 @@ 0.0.1-SNAPSHOT UTF-8 - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT diff --git a/spring-javaformat-eclipse/io.spring.javaformat.eclipse.feature/feature.xml b/spring-javaformat-eclipse/io.spring.javaformat.eclipse.feature/feature.xml index 9f08618a..566fc75f 100755 --- a/spring-javaformat-eclipse/io.spring.javaformat.eclipse.feature/feature.xml +++ b/spring-javaformat-eclipse/io.spring.javaformat.eclipse.feature/feature.xml @@ -2,7 +2,7 @@ @@ -22,7 +22,7 @@ id="io.spring.javaformat.eclipse" download-size="0" install-size="0" - version="0.0.44.qualifier" + version="0.0.46.qualifier" unpack="false"/> diff --git a/spring-javaformat-eclipse/io.spring.javaformat.eclipse.feature/pom.xml b/spring-javaformat-eclipse/io.spring.javaformat.eclipse.feature/pom.xml index 0393db0c..8781b88f 100755 --- a/spring-javaformat-eclipse/io.spring.javaformat.eclipse.feature/pom.xml +++ b/spring-javaformat-eclipse/io.spring.javaformat.eclipse.feature/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat-eclipse - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT io.spring.javaformat.eclipse.feature eclipse-feature diff --git a/spring-javaformat-eclipse/io.spring.javaformat.eclipse.site/category.xml b/spring-javaformat-eclipse/io.spring.javaformat.eclipse.site/category.xml index e3ad51fd..a7901f2c 100644 --- a/spring-javaformat-eclipse/io.spring.javaformat.eclipse.site/category.xml +++ b/spring-javaformat-eclipse/io.spring.javaformat.eclipse.site/category.xml @@ -3,7 +3,7 @@ Maven Integration for Eclipse (maven-eclipse-plugin support) - + diff --git a/spring-javaformat-eclipse/io.spring.javaformat.eclipse.site/io.spring.javaformat.eclipse.product b/spring-javaformat-eclipse/io.spring.javaformat.eclipse.site/io.spring.javaformat.eclipse.product index fc5cf2eb..542c8623 100644 --- a/spring-javaformat-eclipse/io.spring.javaformat.eclipse.site/io.spring.javaformat.eclipse.product +++ b/spring-javaformat-eclipse/io.spring.javaformat.eclipse.site/io.spring.javaformat.eclipse.product @@ -1,7 +1,7 @@ - + diff --git a/spring-javaformat-eclipse/io.spring.javaformat.eclipse.site/pom.xml b/spring-javaformat-eclipse/io.spring.javaformat.eclipse.site/pom.xml index 7d8f71ce..73044003 100755 --- a/spring-javaformat-eclipse/io.spring.javaformat.eclipse.site/pom.xml +++ b/spring-javaformat-eclipse/io.spring.javaformat.eclipse.site/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat-eclipse - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT io.spring.javaformat.eclipse.site eclipse-repository diff --git a/spring-javaformat-eclipse/io.spring.javaformat.eclipse.tests/META-INF/MANIFEST.MF b/spring-javaformat-eclipse/io.spring.javaformat.eclipse.tests/META-INF/MANIFEST.MF index 4f9fbff4..6dc39cf8 100755 --- a/spring-javaformat-eclipse/io.spring.javaformat.eclipse.tests/META-INF/MANIFEST.MF +++ b/spring-javaformat-eclipse/io.spring.javaformat.eclipse.tests/META-INF/MANIFEST.MF @@ -7,7 +7,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Spring Java Format Plugin Tests Bundle-SymbolicName: io.spring.javaformat.eclipse.tests Automatic-Module-Name: io.spring.javaformat.eclipse.tests -Bundle-Version: 0.0.44.qualifier +Bundle-Version: 0.0.46.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ClassPath: ., lib/assertj-core.jar, diff --git a/spring-javaformat-eclipse/io.spring.javaformat.eclipse.tests/pom.xml b/spring-javaformat-eclipse/io.spring.javaformat.eclipse.tests/pom.xml index bf8669b4..3e3b7156 100644 --- a/spring-javaformat-eclipse/io.spring.javaformat.eclipse.tests/pom.xml +++ b/spring-javaformat-eclipse/io.spring.javaformat.eclipse.tests/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat-eclipse - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT io.spring.javaformat.eclipse.tests eclipse-test-plugin diff --git a/spring-javaformat-eclipse/io.spring.javaformat.eclipse/META-INF/MANIFEST.MF b/spring-javaformat-eclipse/io.spring.javaformat.eclipse/META-INF/MANIFEST.MF index 46b89ccb..62909cb6 100644 --- a/spring-javaformat-eclipse/io.spring.javaformat.eclipse/META-INF/MANIFEST.MF +++ b/spring-javaformat-eclipse/io.spring.javaformat.eclipse/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Spring Java Format Plugin Bundle-SymbolicName: io.spring.javaformat.eclipse;singleton:=true Automatic-Module-Name: io.spring.javaformat.eclipse -Bundle-Version: 0.0.44.qualifier +Bundle-Version: 0.0.46.qualifier Bundle-Activator: io.spring.javaformat.eclipse.Activator Bundle-RequiredExecutionEnvironment: JavaSE-11 Require-Bundle: org.eclipse.ui, diff --git a/spring-javaformat-eclipse/io.spring.javaformat.eclipse/pom.xml b/spring-javaformat-eclipse/io.spring.javaformat.eclipse/pom.xml index dec698d9..6c1fa6a7 100644 --- a/spring-javaformat-eclipse/io.spring.javaformat.eclipse/pom.xml +++ b/spring-javaformat-eclipse/io.spring.javaformat.eclipse/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat-eclipse - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT io.spring.javaformat.eclipse eclipse-plugin diff --git a/spring-javaformat-eclipse/pom.xml b/spring-javaformat-eclipse/pom.xml index b0d1b674..1a2639c5 100644 --- a/spring-javaformat-eclipse/pom.xml +++ b/spring-javaformat-eclipse/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat-build - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-eclipse pom diff --git a/spring-javaformat-gradle/io.spring.javaformat.gradle.plugin/pom.xml b/spring-javaformat-gradle/io.spring.javaformat.gradle.plugin/pom.xml index 21ea0f9b..93f26ee8 100644 --- a/spring-javaformat-gradle/io.spring.javaformat.gradle.plugin/pom.xml +++ b/spring-javaformat-gradle/io.spring.javaformat.gradle.plugin/pom.xml @@ -5,7 +5,7 @@ io.spring.javaformat spring-javaformat-gradle - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT io.spring.javaformat io.spring.javaformat.gradle.plugin diff --git a/spring-javaformat-gradle/pom.xml b/spring-javaformat-gradle/pom.xml index e3026aee..39880551 100644 --- a/spring-javaformat-gradle/pom.xml +++ b/spring-javaformat-gradle/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat-build - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-gradle pom diff --git a/spring-javaformat-gradle/spring-javaformat-gradle-plugin/pom.xml b/spring-javaformat-gradle/spring-javaformat-gradle-plugin/pom.xml index f6967ca0..0c7792a2 100644 --- a/spring-javaformat-gradle/spring-javaformat-gradle-plugin/pom.xml +++ b/spring-javaformat-gradle/spring-javaformat-gradle-plugin/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat-gradle - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-gradle-plugin pom diff --git a/spring-javaformat-intellij-idea/pom.xml b/spring-javaformat-intellij-idea/pom.xml index 1daa8cad..42d82863 100644 --- a/spring-javaformat-intellij-idea/pom.xml +++ b/spring-javaformat-intellij-idea/pom.xml @@ -5,7 +5,7 @@ io.spring.javaformat spring-javaformat-build - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-intellij-idea pom diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/pom.xml b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/pom.xml index a1e09a0e..f5581b69 100644 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/pom.xml +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat-intellij-idea - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-intellij-idea-plugin Spring JavaFormat IntelliJ IDEA Plugin diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-runtime/pom.xml b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-runtime/pom.xml index 3e577cbd..bdbf48d4 100644 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-runtime/pom.xml +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-runtime/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat-intellij-idea - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-intellij-idea-runtime pom diff --git a/spring-javaformat-maven/pom.xml b/spring-javaformat-maven/pom.xml index 896f87b8..02900b22 100644 --- a/spring-javaformat-maven/pom.xml +++ b/spring-javaformat-maven/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat-build - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-maven pom diff --git a/spring-javaformat-maven/spring-javaformat-maven-plugin/pom.xml b/spring-javaformat-maven/spring-javaformat-maven-plugin/pom.xml index bdcb704c..100d5a83 100644 --- a/spring-javaformat-maven/spring-javaformat-maven-plugin/pom.xml +++ b/spring-javaformat-maven/spring-javaformat-maven-plugin/pom.xml @@ -5,7 +5,7 @@ io.spring.javaformat spring-javaformat-maven - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-maven-plugin maven-plugin diff --git a/spring-javaformat-vscode/pom.xml b/spring-javaformat-vscode/pom.xml index e87f39f0..ed69c065 100644 --- a/spring-javaformat-vscode/pom.xml +++ b/spring-javaformat-vscode/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat-build - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-vscode pom diff --git a/spring-javaformat-vscode/spring-javaformat-vscode-extension/package-lock.json b/spring-javaformat-vscode/spring-javaformat-vscode-extension/package-lock.json index f991a7ab..7a1dbc61 100644 --- a/spring-javaformat-vscode/spring-javaformat-vscode-extension/package-lock.json +++ b/spring-javaformat-vscode/spring-javaformat-vscode-extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "spring-javaformat-vscode-extension", - "version": "0.0.44-SNAPSHOT", + "version": "0.0.46-SNAPSHOT", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "spring-javaformat-vscode-extension", - "version": "0.0.44-SNAPSHOT", + "version": "0.0.46-SNAPSHOT", "devDependencies": { "@types/glob": "^8.0.1", "@types/mocha": "^10.0.1", diff --git a/spring-javaformat-vscode/spring-javaformat-vscode-extension/package.json b/spring-javaformat-vscode/spring-javaformat-vscode-extension/package.json index 290ae51f..0b7e7feb 100644 --- a/spring-javaformat-vscode/spring-javaformat-vscode-extension/package.json +++ b/spring-javaformat-vscode/spring-javaformat-vscode-extension/package.json @@ -2,7 +2,7 @@ "name": "spring-javaformat-vscode-extension", "description": "Spring JavaFormat Visual Studio Code Extension", "displayName": "Spring JavaFormat", - "version": "0.0.44-SNAPSHOT", + "version": "0.0.46-SNAPSHOT", "publisher": "io.spring.javaformat", "engines": { "vscode": "^1.75.0" diff --git a/spring-javaformat-vscode/spring-javaformat-vscode-extension/pom.xml b/spring-javaformat-vscode/spring-javaformat-vscode-extension/pom.xml index 66239a95..afa9b39a 100644 --- a/spring-javaformat-vscode/spring-javaformat-vscode-extension/pom.xml +++ b/spring-javaformat-vscode/spring-javaformat-vscode-extension/pom.xml @@ -5,7 +5,7 @@ io.spring.javaformat spring-javaformat-vscode - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-vscode-extension Spring JavaFormat Visual Studio Code Extension diff --git a/spring-javaformat/pom.xml b/spring-javaformat/pom.xml index e5991b91..c7815e7c 100644 --- a/spring-javaformat/pom.xml +++ b/spring-javaformat/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat-build - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat pom diff --git a/spring-javaformat/spring-javaformat-checkstyle/pom.xml b/spring-javaformat/spring-javaformat-checkstyle/pom.xml index 3ea8357e..709a2f6a 100644 --- a/spring-javaformat/spring-javaformat-checkstyle/pom.xml +++ b/spring-javaformat/spring-javaformat-checkstyle/pom.xml @@ -5,7 +5,7 @@ io.spring.javaformat spring-javaformat - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-checkstyle Spring JavaFormat CheckStyle diff --git a/spring-javaformat/spring-javaformat-checkstyle/src/main/java/io/spring/javaformat/checkstyle/check/SpringAnnotationLocationCheck.java b/spring-javaformat/spring-javaformat-checkstyle/src/main/java/io/spring/javaformat/checkstyle/check/SpringAnnotationLocationCheck.java new file mode 100644 index 00000000..f0fc0d5e --- /dev/null +++ b/spring-javaformat/spring-javaformat-checkstyle/src/main/java/io/spring/javaformat/checkstyle/check/SpringAnnotationLocationCheck.java @@ -0,0 +1,124 @@ +/* + * Copyright 2017-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.spring.javaformat.checkstyle.check; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import com.puppycrawl.tools.checkstyle.api.AbstractCheck; +import com.puppycrawl.tools.checkstyle.api.DetailAST; +import com.puppycrawl.tools.checkstyle.api.TokenTypes; +import com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationLocationCheck; +import com.puppycrawl.tools.checkstyle.utils.CommonUtil; + +/** + * Spring variant of {@link AnnotationLocationCheck}. + * + * @author Phillip Webb + */ +public class SpringAnnotationLocationCheck extends AbstractCheck { + + private static final Set JSPECIFY_ANNOTATION_NAMES = new HashSet<>( + Arrays.asList("NonNull", "Nullable", "NullMarked", "NullUnmarked")); + + @Override + public int[] getDefaultTokens() { + return new int[] { TokenTypes.CLASS_DEF, TokenTypes.INTERFACE_DEF, TokenTypes.PACKAGE_DEF, + TokenTypes.ENUM_CONSTANT_DEF, TokenTypes.ENUM_DEF, TokenTypes.METHOD_DEF, TokenTypes.CTOR_DEF, + TokenTypes.VARIABLE_DEF, TokenTypes.RECORD_DEF, TokenTypes.COMPACT_CTOR_DEF, }; + } + + @Override + public int[] getAcceptableTokens() { + return new int[] { TokenTypes.CLASS_DEF, TokenTypes.INTERFACE_DEF, TokenTypes.PACKAGE_DEF, + TokenTypes.ENUM_CONSTANT_DEF, TokenTypes.ENUM_DEF, TokenTypes.METHOD_DEF, TokenTypes.CTOR_DEF, + TokenTypes.VARIABLE_DEF, TokenTypes.ANNOTATION_DEF, TokenTypes.ANNOTATION_FIELD_DEF, + TokenTypes.RECORD_DEF, TokenTypes.COMPACT_CTOR_DEF, }; + } + + @Override + public int[] getRequiredTokens() { + return CommonUtil.EMPTY_INT_ARRAY; + } + + @Override + public void visitToken(DetailAST ast) { + if (ast.getType() != TokenTypes.VARIABLE_DEF || ast.getParent().getType() == TokenTypes.OBJBLOCK) { + DetailAST node = ast.findFirstToken(TokenTypes.MODIFIERS); + node = (node != null) ? node : ast.findFirstToken(TokenTypes.ANNOTATIONS); + checkAnnotations(node, getExpectedAnnotationIndentation(node)); + } + } + + private int getExpectedAnnotationIndentation(DetailAST node) { + return node.getColumnNo(); + } + + private void checkAnnotations(DetailAST node, int correctIndentation) { + DetailAST annotation = node.getFirstChild(); + while (annotation != null && annotation.getType() == TokenTypes.ANNOTATION) { + checkAnnotation(correctIndentation, annotation); + annotation = annotation.getNextSibling(); + } + } + + private void checkAnnotation(int correctIndentation, DetailAST annotation) { + String annotationName = getAnnotationName(annotation); + if (!isCorrectLocation(annotation) && !isJSpecifyAnnotation(annotationName)) { + log(annotation, AnnotationLocationCheck.MSG_KEY_ANNOTATION_LOCATION_ALONE, annotationName); + } + else if (annotation.getColumnNo() != correctIndentation && !hasNodeBefore(annotation)) { + log(annotation, AnnotationLocationCheck.MSG_KEY_ANNOTATION_LOCATION, annotationName, + annotation.getColumnNo(), correctIndentation); + } + } + + private String getAnnotationName(DetailAST annotation) { + DetailAST identNode = annotation.findFirstToken(TokenTypes.IDENT); + if (identNode == null) { + identNode = annotation.findFirstToken(TokenTypes.DOT).findFirstToken(TokenTypes.IDENT); + } + return identNode.getText(); + } + + private boolean isCorrectLocation(DetailAST annotation) { + return !hasNodeBeside(annotation); + } + + private boolean hasNodeBeside(DetailAST annotation) { + return hasNodeBefore(annotation) || hasNodeAfter(annotation); + } + + private boolean hasNodeBefore(DetailAST annotation) { + int annotationLineNo = annotation.getLineNo(); + DetailAST previousNode = annotation.getPreviousSibling(); + return (previousNode != null) && (annotationLineNo == previousNode.getLineNo()); + } + + private boolean hasNodeAfter(DetailAST annotation) { + int annotationLineNo = annotation.getLineNo(); + DetailAST nextNode = annotation.getNextSibling(); + nextNode = (nextNode != null) ? nextNode : annotation.getParent().getNextSibling(); + return annotationLineNo == nextNode.getLineNo(); + } + + private boolean isJSpecifyAnnotation(String annotationName) { + return JSPECIFY_ANNOTATION_NAMES.contains(annotationName); + } + +} diff --git a/spring-javaformat/spring-javaformat-checkstyle/src/main/resources/io/spring/javaformat/checkstyle/check/messages.properties b/spring-javaformat/spring-javaformat-checkstyle/src/main/resources/io/spring/javaformat/checkstyle/check/messages.properties index a27f9263..cdea619e 100644 --- a/spring-javaformat/spring-javaformat-checkstyle/src/main/resources/io/spring/javaformat/checkstyle/check/messages.properties +++ b/spring-javaformat/spring-javaformat-checkstyle/src/main/resources/io/spring/javaformat/checkstyle/check/messages.properties @@ -1,3 +1,5 @@ +annotation.location=Annotation ''{0}'' have incorrect indentation level {1}, expected level should be {2}. +annotation.location.alone=Annotation ''{0}'' should be alone on line. catch.singleLetter=Single letter catch variable (use "ex" instead). catch.wideEye=''o_O'' catch variable (use "ex" instead). header.unexpected=Unexpected header. diff --git a/spring-javaformat/spring-javaformat-checkstyle/src/main/resources/io/spring/javaformat/checkstyle/spring-checkstyle.xml b/spring-javaformat/spring-javaformat-checkstyle/src/main/resources/io/spring/javaformat/checkstyle/spring-checkstyle.xml index fa6f8a64..4da52558 100644 --- a/spring-javaformat/spring-javaformat-checkstyle/src/main/resources/io/spring/javaformat/checkstyle/spring-checkstyle.xml +++ b/spring-javaformat/spring-javaformat-checkstyle/src/main/resources/io/spring/javaformat/checkstyle/spring-checkstyle.xml @@ -24,10 +24,7 @@ - - - + diff --git a/spring-javaformat/spring-javaformat-checkstyle/src/test/resources/check/AnnotationOnNewLine.txt b/spring-javaformat/spring-javaformat-checkstyle/src/test/resources/check/AnnotationOnNewLine.txt new file mode 100644 index 00000000..69174e4c --- /dev/null +++ b/spring-javaformat/spring-javaformat-checkstyle/src/test/resources/check/AnnotationOnNewLine.txt @@ -0,0 +1 @@ ++0 errors \ No newline at end of file diff --git a/spring-javaformat/spring-javaformat-checkstyle/src/test/resources/source/AnnotationOnNewLine.java b/spring-javaformat/spring-javaformat-checkstyle/src/test/resources/source/AnnotationOnNewLine.java new file mode 100644 index 00000000..8799aae3 --- /dev/null +++ b/spring-javaformat/spring-javaformat-checkstyle/src/test/resources/source/AnnotationOnNewLine.java @@ -0,0 +1,45 @@ +/* + * Copyright 2017-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.jspecify.annotations.Nullable; + +/** + * This is a valid example. + * + * @author Phillip Webb + */ +public class AnnotationOnNewLine { + + @Override + public String toString() { + return ""; + } + + @Nullable String test1() { + return ""; + } + + @Override + @Nullable String test2() { + return ""; + } + + @Override + public @Nullable String test3() { + return ""; + } + +} diff --git a/spring-javaformat/spring-javaformat-config/pom.xml b/spring-javaformat/spring-javaformat-config/pom.xml index cf59a073..897d4d9a 100644 --- a/spring-javaformat/spring-javaformat-config/pom.xml +++ b/spring-javaformat/spring-javaformat-config/pom.xml @@ -5,7 +5,7 @@ io.spring.javaformat spring-javaformat - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-config Spring JavaFormat Config diff --git a/spring-javaformat/spring-javaformat-formatter-eclipse-jdk17/META-INF/MANIFEST.MF b/spring-javaformat/spring-javaformat-formatter-eclipse-jdk17/META-INF/MANIFEST.MF index abc2e727..b66f5efe 100644 --- a/spring-javaformat/spring-javaformat-formatter-eclipse-jdk17/META-INF/MANIFEST.MF +++ b/spring-javaformat/spring-javaformat-formatter-eclipse-jdk17/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Spring Formatter Eclipse Runtime JDK17 Bundle-SymbolicName: spring-javaformat-formatter-eclipse-jdk17 -Bundle-Version: 0.0.44.qualifier +Bundle-Version: 0.0.46.qualifier Require-Bundle: org.eclipse.jdt.core;bundle-version="[1.0.0,10.0.0)", org.eclipse.jface;bundle-version="[1.0.0,10.0.0)", org.eclipse.jdt.core.source;bundle-version="[1.0.0,10.0.0)";resolution:=optional, diff --git a/spring-javaformat/spring-javaformat-formatter-eclipse-jdk17/pom.xml b/spring-javaformat/spring-javaformat-formatter-eclipse-jdk17/pom.xml index 2eff5105..32c72950 100644 --- a/spring-javaformat/spring-javaformat-formatter-eclipse-jdk17/pom.xml +++ b/spring-javaformat/spring-javaformat-formatter-eclipse-jdk17/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-formatter-eclipse-jdk17 eclipse-plugin diff --git a/spring-javaformat/spring-javaformat-formatter-eclipse-jdk8/META-INF/MANIFEST.MF b/spring-javaformat/spring-javaformat-formatter-eclipse-jdk8/META-INF/MANIFEST.MF index 24f373c1..a6aef027 100644 --- a/spring-javaformat/spring-javaformat-formatter-eclipse-jdk8/META-INF/MANIFEST.MF +++ b/spring-javaformat/spring-javaformat-formatter-eclipse-jdk8/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Spring Formatter Eclipse JDK8 Bundle-SymbolicName: spring-javaformat-formatter-eclipse-jdk8 -Bundle-Version: 0.0.44.qualifier +Bundle-Version: 0.0.46.qualifier Require-Bundle: org.eclipse.jdt.core;bundle-version="[1.0.0,10.0.0)", org.eclipse.jface;bundle-version="[1.0.0,10.0.0)", org.eclipse.jdt.core.source;bundle-version="[1.0.0,10.0.0)";resolution:=optional, diff --git a/spring-javaformat/spring-javaformat-formatter-eclipse-jdk8/pom.xml b/spring-javaformat/spring-javaformat-formatter-eclipse-jdk8/pom.xml index d2ce8030..3d3c8a4c 100644 --- a/spring-javaformat/spring-javaformat-formatter-eclipse-jdk8/pom.xml +++ b/spring-javaformat/spring-javaformat-formatter-eclipse-jdk8/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-formatter-eclipse-jdk8 eclipse-plugin diff --git a/spring-javaformat/spring-javaformat-formatter-eclipse-jdt-jdk17/pom.xml b/spring-javaformat/spring-javaformat-formatter-eclipse-jdt-jdk17/pom.xml index 5e64f7b4..4b0ce32f 100644 --- a/spring-javaformat/spring-javaformat-formatter-eclipse-jdt-jdk17/pom.xml +++ b/spring-javaformat/spring-javaformat-formatter-eclipse-jdt-jdk17/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-formatter-eclipse-jdt-jdk17 Spring JavaFormat Eclipse JDT JDK-17 diff --git a/spring-javaformat/spring-javaformat-formatter-eclipse-jdt-jdk8/pom.xml b/spring-javaformat/spring-javaformat-formatter-eclipse-jdt-jdk8/pom.xml index 88f90cfc..0f557001 100644 --- a/spring-javaformat/spring-javaformat-formatter-eclipse-jdt-jdk8/pom.xml +++ b/spring-javaformat/spring-javaformat-formatter-eclipse-jdt-jdk8/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-formatter-eclipse-jdt-jdk8 Spring JavaFormat Eclipse JDT JDK-8 diff --git a/spring-javaformat/spring-javaformat-formatter-eclipse-rewriter/pom.xml b/spring-javaformat/spring-javaformat-formatter-eclipse-rewriter/pom.xml index 2a686bb6..eb7b4352 100644 --- a/spring-javaformat/spring-javaformat-formatter-eclipse-rewriter/pom.xml +++ b/spring-javaformat/spring-javaformat-formatter-eclipse-rewriter/pom.xml @@ -5,7 +5,7 @@ io.spring.javaformat spring-javaformat - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-formatter-eclipse-rewriter Spring JavaFormat Eclipse Rewriter diff --git a/spring-javaformat/spring-javaformat-formatter-eclipse-runtime/pom.xml b/spring-javaformat/spring-javaformat-formatter-eclipse-runtime/pom.xml index 9be91feb..a9bd4c45 100644 --- a/spring-javaformat/spring-javaformat-formatter-eclipse-runtime/pom.xml +++ b/spring-javaformat/spring-javaformat-formatter-eclipse-runtime/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-formatter-eclipse-runtime Spring JavaFormat Eclipse Runtime diff --git a/spring-javaformat/spring-javaformat-formatter-shaded/pom.xml b/spring-javaformat/spring-javaformat-formatter-shaded/pom.xml index d047564d..13723df1 100644 --- a/spring-javaformat/spring-javaformat-formatter-shaded/pom.xml +++ b/spring-javaformat/spring-javaformat-formatter-shaded/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-formatter-shaded Spring JavaFormat Formatter Shaded diff --git a/spring-javaformat/spring-javaformat-formatter-shader/pom.xml b/spring-javaformat/spring-javaformat-formatter-shader/pom.xml index 6485c73e..2eea794d 100644 --- a/spring-javaformat/spring-javaformat-formatter-shader/pom.xml +++ b/spring-javaformat/spring-javaformat-formatter-shader/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-formatter-shader Spring JavaFormat Formatter Shader diff --git a/spring-javaformat/spring-javaformat-formatter-test-support/pom.xml b/spring-javaformat/spring-javaformat-formatter-test-support/pom.xml index a2315b1f..97190e18 100644 --- a/spring-javaformat/spring-javaformat-formatter-test-support/pom.xml +++ b/spring-javaformat/spring-javaformat-formatter-test-support/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-formatter-test-support Spring JavaFormat Formatter Test Support diff --git a/spring-javaformat/spring-javaformat-formatter-tests/pom.xml b/spring-javaformat/spring-javaformat-formatter-tests/pom.xml index 6a0da60a..e43f5e4c 100644 --- a/spring-javaformat/spring-javaformat-formatter-tests/pom.xml +++ b/spring-javaformat/spring-javaformat-formatter-tests/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-formatter-tests Spring JavaFormat Formatter Tests diff --git a/spring-javaformat/spring-javaformat-formatter-tests/src/test/java/io/spring/javaformat/formatter/AbstractFormatterTests.java b/spring-javaformat/spring-javaformat-formatter-tests/src/test/java/io/spring/javaformat/formatter/AbstractFormatterTests.java index c01324c2..f2bebe0a 100644 --- a/spring-javaformat/spring-javaformat-formatter-tests/src/test/java/io/spring/javaformat/formatter/AbstractFormatterTests.java +++ b/spring-javaformat/spring-javaformat-formatter-tests/src/test/java/io/spring/javaformat/formatter/AbstractFormatterTests.java @@ -21,6 +21,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import io.spring.javaformat.config.JavaBaseline; @@ -60,6 +61,7 @@ protected static Item[] items(String expectedOverride) { addItem(items, javaBaseline, source, expected, config); } } + items.sort(Comparator.comparing(Item::getName)); return items.toArray(new Item[0]); } @@ -139,6 +141,10 @@ private JavaFormatConfig loadConfig(JavaBaseline javaBaseline, File configFile) return JavaFormatConfig.of(javaBaseline, config.getIndentationStyle()); } + String getName() { + return this.source.getName(); + } + public File getSource() { return this.source; } diff --git a/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/expected/nullable-not-jspecify.txt b/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/expected/nullable-not-jspecify.txt new file mode 100644 index 00000000..dbdb1f45 --- /dev/null +++ b/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/expected/nullable-not-jspecify.txt @@ -0,0 +1,23 @@ +package example; + +import com.example.Nullable; + +/** + * Nullable. + * + * @author Phillip Webb + * @since 1.0.0 + */ +public interface ExampleNullables { + + @Override + @Nullable + String myMethod(String param); + + @Override + public @Nullable String myPublicMethod(String param); + + Object myArrayMethod(@Nullable String string, @Nullable String @Nullable [] array, + @Nullable String @Nullable... varargs); + +} diff --git a/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/expected/nullable-wildcard-import.txt b/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/expected/nullable-wildcard-import.txt new file mode 100644 index 00000000..053cbc13 --- /dev/null +++ b/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/expected/nullable-wildcard-import.txt @@ -0,0 +1,22 @@ +package example; + +import org.jspecify.annotations.*; + +/** + * Nullable. + * + * @author Phillip Webb + * @since 1.0.0 + */ +public interface ExampleNullables { + + @Override + @Nullable String myMethod(String param); + + @Override + public @Nullable String myPublicMethod(String param); + + Object myArrayMethod(@Nullable String string, @Nullable String @Nullable [] array, + @Nullable String @Nullable ... varargs); + +} diff --git a/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/expected/nullable.txt b/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/expected/nullable.txt index b077cb5d..b3e8f925 100644 --- a/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/expected/nullable.txt +++ b/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/expected/nullable.txt @@ -16,6 +16,14 @@ public interface ExampleNullables { @Override public @Nullable String myPublicMethod(String param); - Object myArrayMethod(@Nullable String @Nullable [] array, @Nullable String @Nullable ... varargs); + Object myArrayMethod(@Nullable String string, @Nullable String @Nullable [] array, + @Nullable String @Nullable ... varargs); + + default Object inBody() { + @Nullable Object[] args = new Object[length]; + @Nullable List<@Nullable Object> list = new Object[length]; + Object @Nullable [] moreArgs = new Object[length]; + return args; + } } diff --git a/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/source/nullable-not-jspecify.txt b/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/source/nullable-not-jspecify.txt new file mode 100644 index 00000000..011a2189 --- /dev/null +++ b/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/source/nullable-not-jspecify.txt @@ -0,0 +1,20 @@ +package example; + +import com.example.Nullable; + +/** + * Nullable. + * + * @author Phillip Webb + * @since 1.0.0 + */ +public interface ExampleNullables { + + @Override @Nullable String myMethod(String param); + + @Override public @Nullable String myPublicMethod(String param); + + Object myArrayMethod(@Nullable String string, @Nullable String @Nullable [] array, @Nullable + String @Nullable ... varargs); + +} diff --git a/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/source/nullable-wildcard-import.txt b/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/source/nullable-wildcard-import.txt new file mode 100644 index 00000000..11c65198 --- /dev/null +++ b/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/source/nullable-wildcard-import.txt @@ -0,0 +1,20 @@ +package example; + +import org.jspecify.annotations.*; + +/** + * Nullable. + * + * @author Phillip Webb + * @since 1.0.0 + */ +public interface ExampleNullables { + + @Override @Nullable String myMethod(String param); + + @Override public @Nullable String myPublicMethod(String param); + + Object myArrayMethod(@Nullable String string, @Nullable String @Nullable [] array, @Nullable + String @Nullable ... varargs); + +} diff --git a/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/source/nullable.txt b/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/source/nullable.txt index b2952241..acbefead 100644 --- a/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/source/nullable.txt +++ b/spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/source/nullable.txt @@ -14,7 +14,14 @@ public interface ExampleNullables { @Override public @Nullable String myPublicMethod(String param); - Object myArrayMethod(@Nullable String @Nullable [] array, @Nullable + Object myArrayMethod(@Nullable String string, @Nullable String @Nullable [] array, @Nullable String @Nullable ... varargs); + default Object inBody() { + @Nullable Object[] args = new Object[length]; + @Nullable List<@Nullable Object> list = new Object[length]; + Object @Nullable [] moreArgs = new Object[length]; + return args; + } + } diff --git a/spring-javaformat/spring-javaformat-formatter/pom.xml b/spring-javaformat/spring-javaformat-formatter/pom.xml index c604551e..0f2ce215 100644 --- a/spring-javaformat/spring-javaformat-formatter/pom.xml +++ b/spring-javaformat/spring-javaformat-formatter/pom.xml @@ -6,7 +6,7 @@ io.spring.javaformat spring-javaformat - 0.0.44-SNAPSHOT + 0.0.46-SNAPSHOT spring-javaformat-formatter Spring JavaFormat Formatter diff --git a/spring-javaformat/spring-javaformat-formatter/src/main/java/io/spring/javaformat/formatter/jdk17/eclipse/JSpecifyPreparator.java b/spring-javaformat/spring-javaformat-formatter/src/main/java/io/spring/javaformat/formatter/jdk17/eclipse/JSpecifyPreparator.java index 7d017570..645bf75d 100644 --- a/spring-javaformat/spring-javaformat-formatter/src/main/java/io/spring/javaformat/formatter/jdk17/eclipse/JSpecifyPreparator.java +++ b/spring-javaformat/spring-javaformat-formatter/src/main/java/io/spring/javaformat/formatter/jdk17/eclipse/JSpecifyPreparator.java @@ -17,25 +17,38 @@ package io.spring.javaformat.formatter.jdk17.eclipse; import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import io.spring.javaformat.eclipse.jdt.jdk17.core.dom.ASTNode; import io.spring.javaformat.eclipse.jdt.jdk17.core.dom.ASTVisitor; import io.spring.javaformat.eclipse.jdt.jdk17.core.dom.Annotation; +import io.spring.javaformat.eclipse.jdt.jdk17.core.dom.CompilationUnit; import io.spring.javaformat.eclipse.jdt.jdk17.core.dom.IExtendedModifier; +import io.spring.javaformat.eclipse.jdt.jdk17.core.dom.ImportDeclaration; import io.spring.javaformat.eclipse.jdt.jdk17.core.dom.MethodDeclaration; import io.spring.javaformat.eclipse.jdt.jdk17.core.dom.SingleVariableDeclaration; +import io.spring.javaformat.eclipse.jdt.jdk17.core.dom.VariableDeclarationStatement; import io.spring.javaformat.eclipse.jdt.jdk17.core.formatter.CodeFormatter; import io.spring.javaformat.eclipse.jdt.jdk17.internal.formatter.Preparator; import io.spring.javaformat.eclipse.jdt.jdk17.internal.formatter.TokenManager; public class JSpecifyPreparator implements Preparator { + private static final String PACKAGE_NAME = "org.jspecify.annotations"; + private static final Set ANNOTATION_NAMES = new HashSet<>( Arrays.asList("NonNull", "Nullable", "NullMarked", "NullUnmarked")); + private static final Set FULLY_QUALIFIED_ANNOTATION_NAMES = ANNOTATION_NAMES.stream() + .map((annotationName) -> PACKAGE_NAME + "." + annotationName) + .collect(Collectors.toSet()); + @Override public void apply(int kind, TokenManager tokenManager, ASTNode astRoot) { if ((kind & CodeFormatter.K_COMPILATION_UNIT) != 0) { @@ -48,17 +61,40 @@ private static class Vistor extends ASTVisitor { private final TokenManager tokenManager; + private final Map fullyQualified = new HashMap<>(); + Vistor(TokenManager tokenManager) { this.tokenManager = tokenManager; } @Override - @SuppressWarnings("unchecked") - public boolean visit(MethodDeclaration node) { - Annotation lastAnnotation = getLastAnnotation((List) node.modifiers()); - if (isJSpecifyAnnotation(lastAnnotation)) { - this.tokenManager.lastTokenIn(lastAnnotation, -1).clearLineBreaksAfter(); + public boolean visit(CompilationUnit node) { + this.fullyQualified.clear(); + return super.visit(node); + } + + @Override + public boolean visit(ImportDeclaration node) { + String name = node.getName().toString(); + if (name.equals(PACKAGE_NAME) || name.startsWith(PACKAGE_NAME)) { + Set annotationNames = (node.isOnDemand()) ? ANNOTATION_NAMES + : Collections.singleton(name.substring(name.lastIndexOf(".") + 1)); + for (String annotationName : annotationNames) { + this.fullyQualified.put(annotationName, PACKAGE_NAME + "." + annotationName); + } } + return super.visit(node); + } + + @Override + public boolean visit(MethodDeclaration node) { + clearLineBreaksIfHasJSpecifyAnnotation(node.modifiers()); + return true; + } + + @Override + public boolean visit(VariableDeclarationStatement node) { + clearLineBreaksIfHasJSpecifyAnnotation(node.modifiers()); return true; } @@ -74,6 +110,14 @@ public void endVisit(SingleVariableDeclaration node) { } } + @SuppressWarnings("unchecked") + private void clearLineBreaksIfHasJSpecifyAnnotation(List modifiers) { + Annotation lastAnnotation = getLastAnnotation((List) modifiers); + if (isJSpecifyAnnotation(lastAnnotation)) { + this.tokenManager.lastTokenIn(lastAnnotation, -1).clearLineBreaksAfter(); + } + } + private Annotation getLastAnnotation(List modifiers) { Annotation annotation = null; for (IExtendedModifier modifier : modifiers) { @@ -86,7 +130,9 @@ private Annotation getLastAnnotation(List modifiers } private boolean isJSpecifyAnnotation(Annotation annotation) { - return (annotation != null) && ANNOTATION_NAMES.contains(annotation.getTypeName().toString()); + String fullyQualifiedName = (annotation != null) + ? this.fullyQualified.get(annotation.getTypeName().toString()) : null; + return (fullyQualifiedName != null) && FULLY_QUALIFIED_ANNOTATION_NAMES.contains(fullyQualifiedName); } } diff --git a/spring-javaformat/spring-javaformat-formatter/src/main/java/io/spring/javaformat/formatter/jdk8/eclipse/JSpecifyPreparator.java b/spring-javaformat/spring-javaformat-formatter/src/main/java/io/spring/javaformat/formatter/jdk8/eclipse/JSpecifyPreparator.java index 620b462f..4bc1aa0e 100644 --- a/spring-javaformat/spring-javaformat-formatter/src/main/java/io/spring/javaformat/formatter/jdk8/eclipse/JSpecifyPreparator.java +++ b/spring-javaformat/spring-javaformat-formatter/src/main/java/io/spring/javaformat/formatter/jdk8/eclipse/JSpecifyPreparator.java @@ -17,25 +17,38 @@ package io.spring.javaformat.formatter.jdk8.eclipse; import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import io.spring.javaformat.eclipse.jdt.jdk8.core.dom.ASTNode; import io.spring.javaformat.eclipse.jdt.jdk8.core.dom.ASTVisitor; import io.spring.javaformat.eclipse.jdt.jdk8.core.dom.Annotation; +import io.spring.javaformat.eclipse.jdt.jdk8.core.dom.CompilationUnit; import io.spring.javaformat.eclipse.jdt.jdk8.core.dom.IExtendedModifier; +import io.spring.javaformat.eclipse.jdt.jdk8.core.dom.ImportDeclaration; import io.spring.javaformat.eclipse.jdt.jdk8.core.dom.MethodDeclaration; import io.spring.javaformat.eclipse.jdt.jdk8.core.dom.SingleVariableDeclaration; +import io.spring.javaformat.eclipse.jdt.jdk8.core.dom.VariableDeclarationStatement; import io.spring.javaformat.eclipse.jdt.jdk8.core.formatter.CodeFormatter; import io.spring.javaformat.eclipse.jdt.jdk8.internal.formatter.Preparator; import io.spring.javaformat.eclipse.jdt.jdk8.internal.formatter.TokenManager; public class JSpecifyPreparator implements Preparator { + private static final String PACKAGE_NAME = "org.jspecify.annotations"; + private static final Set ANNOTATION_NAMES = new HashSet<>( Arrays.asList("NonNull", "Nullable", "NullMarked", "NullUnmarked")); + private static final Set FULLY_QUALIFIED_ANNOTATION_NAMES = ANNOTATION_NAMES.stream() + .map((annotationName) -> PACKAGE_NAME + "." + annotationName) + .collect(Collectors.toSet()); + @Override public void apply(int kind, TokenManager tokenManager, ASTNode astRoot) { if ((kind & CodeFormatter.K_COMPILATION_UNIT) != 0) { @@ -48,17 +61,40 @@ private static class Vistor extends ASTVisitor { private final TokenManager tokenManager; + private final Map fullyQualified = new HashMap<>(); + Vistor(TokenManager tokenManager) { this.tokenManager = tokenManager; } @Override - @SuppressWarnings("unchecked") - public boolean visit(MethodDeclaration node) { - Annotation lastAnnotation = getLastAnnotation((List) node.modifiers()); - if (isJSpecifyAnnotation(lastAnnotation)) { - this.tokenManager.lastTokenIn(lastAnnotation, -1).clearLineBreaksAfter(); + public boolean visit(CompilationUnit node) { + this.fullyQualified.clear(); + return super.visit(node); + } + + @Override + public boolean visit(ImportDeclaration node) { + String name = node.getName().toString(); + if (name.equals(PACKAGE_NAME) || name.startsWith(PACKAGE_NAME)) { + Set annotationNames = (node.isOnDemand()) ? ANNOTATION_NAMES + : Collections.singleton(name.substring(name.lastIndexOf(".") + 1)); + for (String annotationName : annotationNames) { + this.fullyQualified.put(annotationName, PACKAGE_NAME + "." + annotationName); + } } + return super.visit(node); + } + + @Override + public boolean visit(MethodDeclaration node) { + clearLineBreaksIfHasJSpecifyAnnotation(node.modifiers()); + return true; + } + + @Override + public boolean visit(VariableDeclarationStatement node) { + clearLineBreaksIfHasJSpecifyAnnotation(node.modifiers()); return true; } @@ -74,6 +110,14 @@ public void endVisit(SingleVariableDeclaration node) { } } + @SuppressWarnings("unchecked") + private void clearLineBreaksIfHasJSpecifyAnnotation(List modifiers) { + Annotation lastAnnotation = getLastAnnotation((List) modifiers); + if (isJSpecifyAnnotation(lastAnnotation)) { + this.tokenManager.lastTokenIn(lastAnnotation, -1).clearLineBreaksAfter(); + } + } + private Annotation getLastAnnotation(List modifiers) { Annotation annotation = null; for (IExtendedModifier modifier : modifiers) { @@ -86,7 +130,9 @@ private Annotation getLastAnnotation(List modifiers } private boolean isJSpecifyAnnotation(Annotation annotation) { - return (annotation != null) && ANNOTATION_NAMES.contains(annotation.getTypeName().toString()); + String fullyQualifiedName = (annotation != null) + ? this.fullyQualified.get(annotation.getTypeName().toString()) : null; + return (fullyQualifiedName != null) && FULLY_QUALIFIED_ANNOTATION_NAMES.contains(fullyQualifiedName); } }