Skip to content

Commit d279590

Browse files
PR for Unsupported Type for @Version Field (#1597)
* Diagnostic for version type * version type test case source * Version type test cases * revert gitignore * Add bob folder to ignore * Diagnostics code changes * Address PR comments Constant SUPPORTED_VERSION_TYPES to Set Test case for timestamp type
1 parent 4adcda9 commit d279590

10 files changed

Lines changed: 292 additions & 1 deletion

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ video
77
.vscode
88
.DS_Store
99
bin/
10+
.bob

src/main/java/io/openliberty/tools/intellij/lsp4jakarta/lsp4ij/persistence/PersistenceConstants.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
package io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.persistence;
1515

16+
import java.util.Set;
17+
1618
public class PersistenceConstants {
1719
/* Annotation Constants */
1820
public static final String OBJECT = "java.lang.Object";
@@ -44,6 +46,7 @@ public class PersistenceConstants {
4446
public static final String DIAGNOSTIC_CODE_MISSING_TEMPORAL = "MissingTemporalAnnotation";
4547
public static final String DIAGNOSTIC_CODE_DUPLICATE_VERSION = "MultipleVersionAnnotations";
4648
public static final String DIAGNOSTIC_CODE_VERSION_IN_HIERARCHY = "VersionAnnotationInHierarchy";
49+
public static final String DIAGNOSTIC_CODE_INVALID_VERSION_TYPE = "InvalidVersionFieldOrPropertyType";
4750

4851

4952
/* MapKey Codes */
@@ -57,7 +60,9 @@ public class PersistenceConstants {
5760

5861
public final static String[] SET_OF_PERSISTENCE_ANNOTATIONS = {MAPKEY, MAPKEYCLASS, MAPKEYJOINCOLUMN};
5962
public static final String[] SET_OF_PRIMARY_KEY_DATE_ANNOTATIONS = { ID, TEMPORAL };
60-
63+
public static final Set<String> SET_OF_VALID_VERSION_TYPES = Set.of(
64+
"int", "short", "long", "java.lang.Integer",
65+
"java.lang.Short", "java.lang.Long", "java.sql.Timestamp");
6166
public static final String UTIL_DATE = "java.util.Date";
6267
public static final String TEMPORAL_TYPE_DATE = "TemporalType.DATE";
6368
}

src/main/java/io/openliberty/tools/intellij/lsp4jakarta/lsp4ij/persistence/PersistenceEntityDiagnosticsCollector.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ public void collectDiagnostics(PsiJavaFile unit, List<Diagnostic> diagnostics) {
7474
// find @Version annotation usage on methods
7575
if (isMatchedAnnotation(method.getAnnotations(), PersistenceConstants.VERSION)) {
7676
versionAnnotatedElements.add(method);
77+
// Validate @Version method return type
78+
validateVersionType(method, unit, diagnostics);
7779
}
7880

7981
if (isConstructorMethod(method)) {
@@ -110,6 +112,8 @@ public void collectDiagnostics(PsiJavaFile unit, List<Diagnostic> diagnostics) {
110112
// find @Version annotation usage on fields
111113
if (isMatchedAnnotation(field.getAnnotations(), PersistenceConstants.VERSION)) {
112114
versionAnnotatedElements.add(field);
115+
// Validate @Version field type
116+
validateVersionType(field, unit, diagnostics);
113117
}
114118

115119
// If a field is static, we do not care about it, we care about all other field
@@ -394,4 +398,41 @@ private boolean hasPrimaryKeyInSuperclass(PsiClass type) {
394398
return false;
395399
}
396400

401+
/**
402+
* Validates that a field or method annotated with @Version has a supported type.
403+
* For fields: validates the field type
404+
* For methods: validates the return type
405+
* Supported types: int, Integer, short, Short, long, Long, java.sql.Timestamp
406+
*
407+
* @param element the field or method annotated with @Version
408+
* @param unit compilation unit of Java class
409+
* @param diagnostics list to add diagnostics to
410+
*/
411+
private void validateVersionType(PsiJvmModifiersOwner element, PsiJavaFile unit, List<Diagnostic> diagnostics) {
412+
PsiType elementType = null;
413+
414+
// Get the type based on whether it's a field or method
415+
if (element instanceof PsiField) {
416+
elementType = ((PsiField) element).getType();
417+
} else if (element instanceof PsiMethod) {
418+
elementType = ((PsiMethod) element).getReturnType();
419+
}
420+
421+
// If we couldn't determine the type, skip validation
422+
if (elementType == null) {
423+
return;
424+
}
425+
426+
String typeName = elementType.getCanonicalText();
427+
428+
// Check if type is in the list of supported types
429+
boolean isValidType = PersistenceConstants.SET_OF_VALID_VERSION_TYPES.contains(typeName);
430+
431+
if (!isValidType) {
432+
diagnostics.add(createDiagnostic(element, unit,
433+
Messages.getMessage("InvalidVersionFieldOrPropertyType"),
434+
PersistenceConstants.DIAGNOSTIC_CODE_INVALID_VERSION_TYPE, null,
435+
DiagnosticSeverity.Error));
436+
}
437+
}
397438
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ MissingTemporalAnnotation = A field or property marked with @Id and of type java
195195
InvalidValueInTemporalAnnotation = The @Temporal annotation on a field or property annotated with @Id and of type java.util.Date must specify TemporalType.DATE.
196196
DuplicateVersionAnnotation = Multiple fields or properties are annotated with @Version. Only one @Version annotation is allowed per entity class.
197197
VersionAnnotationInHierarchy = A @Version annotation is already present in the entity hierarchy. Only one @Version annotation is allowed across the entire entity inheritance hierarchy.
198+
InvalidVersionFieldOrPropertyType = A field or property annotated with @Version must be of type int, Integer, short, Short, long, Long, or java.sql.Timestamp.
198199

199200
# PersistenceEntityQuickFix
200201
AddNoArgProtectedConstructor = Add a no-arg protected constructor to this class

src/test/java/io/openliberty/tools/intellij/lsp4jakarta/it/persistence/JakartaPersistenceTest.java

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,5 +715,92 @@ public void testVersionAnnotationInHierarchy() throws Exception {
715715

716716
assertJavaDiagnostics(diagnosticsParams, utils, versionInHierarchyD1);
717717
}
718+
@Test
719+
public void testInvalidVersionFieldType() throws Exception {
720+
Module module = createMavenModule(new File("src/test/resources/projects/maven/jakarta-sample"));
721+
IPsiUtils utils = PsiUtilsLSImpl.getInstance(getProject());
722+
723+
VirtualFile javaFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(ModuleUtilCore.getModuleDirPath(module)
724+
+ "/src/main/java/io/openliberty/sample/jakarta/persistence/EntityInvalidVersionFieldType.java");
725+
String uri = VfsUtilCore.virtualToIoFile(javaFile).toURI().toString();
726+
727+
JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams();
728+
diagnosticsParams.setUris(Arrays.asList(uri));
729+
730+
// Test diagnostic for invalid @Version field type (String)
731+
Diagnostic invalidVersionTypeD1 = d(13, 19, 26,
732+
"A field or property annotated with @Version must be of type int, Integer, short, Short, long, Long, or java.sql.Timestamp.",
733+
DiagnosticSeverity.Error, "jakarta-persistence", "InvalidVersionFieldOrPropertyType");
734+
735+
assertJavaDiagnostics(diagnosticsParams, utils, invalidVersionTypeD1);
736+
}
737+
738+
@Test
739+
public void testValidVersionFieldType() throws Exception {
740+
Module module = createMavenModule(new File("src/test/resources/projects/maven/jakarta-sample"));
741+
IPsiUtils utils = PsiUtilsLSImpl.getInstance(getProject());
742+
743+
VirtualFile javaFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(ModuleUtilCore.getModuleDirPath(module)
744+
+ "/src/main/java/io/openliberty/sample/jakarta/persistence/EntityValidVersionFieldType.java");
745+
String uri = VfsUtilCore.virtualToIoFile(javaFile).toURI().toString();
746+
747+
JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams();
748+
diagnosticsParams.setUris(Arrays.asList(uri));
749+
750+
// Test that no diagnostics are generated for valid @Version field type (int)
751+
assertJavaDiagnostics(diagnosticsParams, utils);
752+
}
753+
754+
@Test
755+
public void testInvalidVersionMethodType() throws Exception {
756+
Module module = createMavenModule(new File("src/test/resources/projects/maven/jakarta-sample"));
757+
IPsiUtils utils = PsiUtilsLSImpl.getInstance(getProject());
758+
759+
VirtualFile javaFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(ModuleUtilCore.getModuleDirPath(module)
760+
+ "/src/main/java/io/openliberty/sample/jakarta/persistence/EntityInvalidVersionMethodType.java");
761+
String uri = VfsUtilCore.virtualToIoFile(javaFile).toURI().toString();
762+
763+
JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams();
764+
diagnosticsParams.setUris(Arrays.asList(uri));
765+
766+
// Test diagnostic for invalid @Version method return type (String)
767+
Diagnostic invalidVersionTypeD1 = d(26, 18, 28,
768+
"A field or property annotated with @Version must be of type int, Integer, short, Short, long, Long, or java.sql.Timestamp.",
769+
DiagnosticSeverity.Error, "jakarta-persistence", "InvalidVersionFieldOrPropertyType");
770+
771+
assertJavaDiagnostics(diagnosticsParams, utils, invalidVersionTypeD1);
772+
}
773+
774+
@Test
775+
public void testValidVersionMethodType() throws Exception {
776+
Module module = createMavenModule(new File("src/test/resources/projects/maven/jakarta-sample"));
777+
IPsiUtils utils = PsiUtilsLSImpl.getInstance(getProject());
778+
779+
VirtualFile javaFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(ModuleUtilCore.getModuleDirPath(module)
780+
+ "/src/main/java/io/openliberty/sample/jakarta/persistence/EntityValidVersionMethodType.java");
781+
String uri = VfsUtilCore.virtualToIoFile(javaFile).toURI().toString();
782+
783+
JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams();
784+
diagnosticsParams.setUris(Arrays.asList(uri));
785+
786+
// Test that no diagnostics are generated for valid @Version method return type (long)
787+
assertJavaDiagnostics(diagnosticsParams, utils);
788+
}
789+
790+
@Test
791+
public void testValidVersionTimestampType() throws Exception {
792+
Module module = createMavenModule(new File("src/test/resources/projects/maven/jakarta-sample"));
793+
IPsiUtils utils = PsiUtilsLSImpl.getInstance(getProject());
794+
795+
VirtualFile javaFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(ModuleUtilCore.getModuleDirPath(module)
796+
+ "/src/main/java/io/openliberty/sample/jakarta/persistence/EntityValidVersionTimestamp.java");
797+
String uri = VfsUtilCore.virtualToIoFile(javaFile).toURI().toString();
798+
799+
JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams();
800+
diagnosticsParams.setUris(Arrays.asList(uri));
801+
802+
// Test that no diagnostics are generated for valid @Version method return type (Timestamp)
803+
assertJavaDiagnostics(diagnosticsParams, utils);
804+
}
718805

719806
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.openliberty.sample.jakarta.persistence;
2+
3+
import jakarta.persistence.Entity;
4+
import jakarta.persistence.Id;
5+
import jakarta.persistence.Version;
6+
7+
@Entity
8+
public class EntityInvalidVersionFieldType {
9+
10+
@Id
11+
private Long id;
12+
13+
@Version
14+
private String version; // Invalid: String is not a supported type
15+
16+
public EntityInvalidVersionFieldType() {
17+
}
18+
19+
public Long getId() {
20+
return id;
21+
}
22+
23+
public void setId(Long id) {
24+
this.id = id;
25+
}
26+
27+
public String getVersion() {
28+
return version;
29+
}
30+
31+
public void setVersion(String version) {
32+
this.version = version;
33+
}
34+
}
35+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.openliberty.sample.jakarta.persistence;
2+
3+
import jakarta.persistence.Entity;
4+
import jakarta.persistence.Id;
5+
import jakarta.persistence.Version;
6+
7+
@Entity
8+
public class EntityInvalidVersionMethodType {
9+
10+
@Id
11+
private Long id;
12+
13+
private String version; // Invalid: String is not a supported type
14+
15+
public EntityInvalidVersionMethodType() {
16+
}
17+
18+
public Long getId() {
19+
return id;
20+
}
21+
22+
public void setId(Long id) {
23+
this.id = id;
24+
}
25+
26+
@Version
27+
public String getVersion() { // Invalid: String return type
28+
return version;
29+
}
30+
31+
public void setVersion(String version) {
32+
this.version = version;
33+
}
34+
}
35+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.openliberty.sample.jakarta.persistence;
2+
3+
import jakarta.persistence.Entity;
4+
import jakarta.persistence.Id;
5+
import jakarta.persistence.Version;
6+
import java.sql.Timestamp;
7+
8+
@Entity
9+
public class EntityValidVersionFieldType {
10+
11+
@Id
12+
private Long id;
13+
14+
@Version
15+
private int version; // Valid: int is a supported type
16+
17+
public EntityValidVersionFieldType() {
18+
}
19+
20+
public Long getId() {
21+
return id;
22+
}
23+
24+
public void setId(Long id) {
25+
this.id = id;
26+
}
27+
28+
public int getVersion() {
29+
return version;
30+
}
31+
32+
public void setVersion(int version) {
33+
this.version = version;
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.openliberty.sample.jakarta.persistence;
2+
3+
import jakarta.persistence.Entity;
4+
import jakarta.persistence.Id;
5+
import jakarta.persistence.Version;
6+
7+
@Entity
8+
public class EntityValidVersionMethodType {
9+
10+
@Id
11+
private Long id;
12+
13+
private long version; // Valid: long is a supported type
14+
15+
public EntityValidVersionMethodType() {
16+
}
17+
18+
public Long getId() {
19+
return id;
20+
}
21+
22+
public void setId(Long id) {
23+
this.id = id;
24+
}
25+
26+
@Version
27+
public long getVersion() { // Valid: long return type
28+
return version;
29+
}
30+
31+
public void setVersion(long version) {
32+
this.version = version;
33+
}
34+
}
35+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package io.openliberty.sample.jakarta.persistence.version;
2+
3+
import jakarta.persistence.Entity;
4+
import jakarta.persistence.Id;
5+
import jakarta.persistence.Version;
6+
import java.sql.Timestamp;
7+
8+
@Entity
9+
public class EntityValidVersionTimestamp {
10+
@Id
11+
private Long id;
12+
13+
@Version
14+
private Timestamp version; // Valid - java.sql.Timestamp
15+
}
16+

0 commit comments

Comments
 (0)