Skip to content

Commit fea63e1

Browse files
authored
Feature/size and not empty annotation diagnostics (#1410)
* Introduced validation for size and notEmpty sds * Update messages.properties * size and notEmpty test cases * size and notEmpty test resources * pr comments * Update BeanValidationTest.java * corrected diagnostics grammer * pr comments * pr comments addressed * PR comments
1 parent 98fb0b9 commit fea63e1

File tree

8 files changed

+250
-96
lines changed

8 files changed

+250
-96
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 IBM Corporation
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* IBM Corporation - initial API and implementation
12+
*******************************************************************************/
13+
14+
package io.openliberty.tools.intellij.lsp4jakarta.lsp4ij;
15+
16+
import com.intellij.openapi.project.Project;
17+
import com.intellij.psi.*;
18+
import com.intellij.psi.search.GlobalSearchScope;
19+
20+
/**
21+
* Utility class for common IntelliJ PSI-based diagnostic logic.
22+
*/
23+
public class DiagnosticsUtils {
24+
25+
/**
26+
* inheritsFrom
27+
* Check if specified superType is present or not in the type hierarchy
28+
*
29+
* @param clazz
30+
* @param fqSuperType
31+
* @return
32+
*/
33+
public static boolean inheritsFrom(PsiClass clazz, String fqSuperType) {
34+
Project project = clazz.getProject();
35+
PsiClass superClass = JavaPsiFacade.getInstance(project)
36+
.findClass(fqSuperType, GlobalSearchScope.allScope(project));
37+
return superClass != null &&
38+
(clazz.isEquivalentTo(superClass) || clazz.isInheritor(superClass, true));
39+
}
40+
}

src/main/java/io/openliberty/tools/intellij/lsp4jakarta/lsp4ij/beanvalidation/BeanValidationConstants.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2020, 2024 IBM Corporation, Reza Akhavan and others.
2+
* Copyright (c) 2020, 2025 IBM Corporation, Reza Akhavan and others.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -63,6 +63,8 @@ public class BeanValidationConstants {
6363
public static final String STRING = "java.lang.String";
6464
public static final String BIG_INTEGER = "java.math.BigInteger";
6565
public static final String BIG_DECIMAL = "java.math.BigDecimal";
66+
public static final String COLLECTION_FQ = "java.util.Collection";
67+
public static final String MAP_FQ = "java.util.Map";
6668

6769
public static final String DIAGNOSTIC_SOURCE = "jakarta-bean-validation";
6870
public static final String DIAGNOSTIC_CODE_INVALID_TYPE = "FixTypeOfElement";

src/main/java/io/openliberty/tools/intellij/lsp4jakarta/lsp4ij/beanvalidation/BeanValidationDiagnosticsCollector.java

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2020, 2024 IBM Corporation, Reza Akhavan and others.
2+
* Copyright (c) 2020, 2025 IBM Corporation, Reza Akhavan and others.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -14,11 +14,13 @@
1414
package io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.beanvalidation;
1515

1616
import com.intellij.psi.*;
17+
import com.intellij.psi.util.PsiUtil;
1718
import io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.AbstractDiagnosticsCollector;
1819
import io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.Messages;
1920
import org.eclipse.lsp4j.Diagnostic;
2021
import org.eclipse.lsp4j.DiagnosticSeverity;
2122

23+
import static io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.DiagnosticsUtils.inheritsFrom;
2224
import static io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.JDTUtils.getSimpleName;
2325
import static io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.beanvalidation.BeanValidationConstants.*;
2426

@@ -159,40 +161,40 @@ private void validAnnotation(PsiElement element, PsiAnnotation annotation, Strin
159161
diagnostics.add(createDiagnostic(element, (PsiJavaFile) element.getContainingFile(),
160162
source, DIAGNOSTIC_CODE_INVALID_TYPE, annotationName, DiagnosticSeverity.Error));
161163
}
164+
} else if (matchedAnnotation.equals(NOT_EMPTY) || matchedAnnotation.equals(SIZE)) {
165+
if (!(isSizeOrNonEmptyAllowed(type))) {
166+
String source = isMethod ?
167+
Messages.getMessage("SizeOrNonEmptyAnnotationsMethod") : Messages.getMessage(
168+
"SizeOrNonEmptyAnnotationsField");
169+
diagnostics.add(createDiagnostic(element, (PsiJavaFile) element.getContainingFile(),
170+
source, DIAGNOSTIC_CODE_INVALID_TYPE, annotationName, DiagnosticSeverity.Error));
171+
}
162172
}
163-
164-
// These ones contains check on all collection types which requires resolving
165-
// the String of the type somehow
166-
// This will also require us to check if the field type was a custom collection
167-
// subtype which means we
168-
// have to resolve it and get the super interfaces and check to see if
169-
// Collection, Map or Array was implemented
170-
// for that custom type (which could as well be a user made subtype)
171-
172-
// else if (annotation.getElementName().equals(NOT_EMPTY) || annotation.getElementName().equals(SIZE)) {
173-
//
174-
// System.out.println("--Field name: " + Signature.getTypeSignatureKind(fieldType));
175-
// System.out.println("--Field name: " + Signature.getParameterTypes(fieldType));
176-
// if ( !fieldType.equals(getSignatureFormatOfType(CHAR_SEQUENCE)) &&
177-
// !fieldType.contains("List") &&
178-
// !fieldType.contains("Set") &&
179-
// !fieldType.contains("Collection") &&
180-
// !fieldType.contains("Array") &&
181-
// !fieldType.contains("Vector") &&
182-
// !fieldType.contains("Stack") &&
183-
// !fieldType.contains("Queue") &&
184-
// !fieldType.contains("Deque") &&
185-
// !fieldType.contains("Map")) {
186-
//
187-
// diagnostics.add(new Diagnostic(fieldAnnotationrange,
188-
// "This annotation can only be used on CharSequence, Collection, Array, "
189-
// + "Map type fields."));
190-
// }
191-
// }
192173
}
193174
}
194175
}
195176

177+
/**
178+
* isSizeOrNonEmptyAllowed
179+
* This method checks whether the supported types for the Size and NotEmpty annotations are CharSequence, Collection, Map, or array.
180+
*
181+
* @param childType
182+
* @return
183+
*/
184+
public static boolean isSizeOrNonEmptyAllowed(PsiType childType) {
185+
186+
if (childType instanceof PsiArrayType) {
187+
return true;
188+
}
189+
if (childType instanceof PsiPrimitiveType) {
190+
return false;
191+
}
192+
PsiClass resolvedClass = PsiUtil.resolveClassInClassTypeOnly(childType);
193+
return resolvedClass != null && (inheritsFrom(resolvedClass, CHAR_SEQUENCE)
194+
|| inheritsFrom(resolvedClass, COLLECTION_FQ)
195+
|| inheritsFrom(resolvedClass, MAP_FQ));
196+
}
197+
196198
private void checkStringOnly(PsiElement element, List<Diagnostic> diagnostics, String annotationName, boolean isMethod, PsiType type) {
197199
if (!type.getCanonicalText().equals(STRING)
198200
&& !type.getCanonicalText().equals(CHAR_SEQUENCE)) {

src/main/resources/io/openliberty/tools/intellij/lsp4jakarta/messages/messages.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ AnnotationStringMethods = The {0} annotation can only be used on String and Char
6565
AnnotationStringFields = The {0} annotation can only be used on String and CharSequence type fields.
6666
AnnotationDateMethods = The {0} annotation can only be used on: Date, Calendar, Instant, LocalDate, LocalDateTime, LocalTime, MonthDay, OffsetDateTime, OffsetTime, Year, YearMonth, ZonedDateTime, HijrahDate, JapaneseDate, JapaneseDate, MinguoDate and ThaiBuddhistDate type methods.
6767
AnnotationDateFields = The {0} annotation can only be used on: Date, Calendar, Instant, LocalDate, LocalDateTime, LocalTime, MonthDay, OffsetDateTime, OffsetTime, Year, YearMonth, ZonedDateTime, HijrahDate, JapaneseDate, JapaneseDate, MinguoDate and ThaiBuddhistDate type fields.
68+
SizeOrNonEmptyAnnotationsField = This annotation can only be used on fields of type CharSequence, Collection, Array, or Map.
69+
SizeOrNonEmptyAnnotationsMethod = This annotation can only be used on methods that have CharSequence, Collection, Array or Map as a return type.
6870
# The next two messages do not include CharSequence
6971
AnnotationMinMaxMethods = The {0} annotation can only be used on \n- BigDecimal \n- BigInteger\n- byte, short, int, long (and their respective wrappers) \n type methods.
7072
AnnotationMinMaxFields = The {0} annotation can only be used on \n- BigDecimal \n- BigInteger\n- byte, short, int, long (and their respective wrappers) \n type fields.

0 commit comments

Comments
 (0)