diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/plugin.xml b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/plugin.xml
index 0486cffc..dbd4ff75 100644
--- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/plugin.xml
+++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/plugin.xml
@@ -58,6 +58,9 @@
+
@@ -84,7 +87,7 @@
class="org.eclipse.lsp4jakarta.jdt.internal.annotations.RemovePreDestroyAnnotationQuickFix" />
+ class="org.eclipse.lsp4jakarta.jdt.internal.annotations.RemoveAllMethodParametersQuickFix" />
diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/codeaction/JakartaCodeActionId.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/codeaction/JakartaCodeActionId.java
index 0bac83b9..46b3f9fd 100644
--- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/codeaction/JakartaCodeActionId.java
+++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/codeaction/JakartaCodeActionId.java
@@ -1,5 +1,5 @@
/*******************************************************************************
-* Copyright (c) 2023 Red Hat Inc. and others.
+* Copyright (c) 2023, 2024 Red Hat Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -28,6 +28,7 @@ public enum JakartaCodeActionId implements ICodeActionId {
ChangeReturnTypeToVoid,
InsertResourceAnnotationTypeAttribute,
InsertResourceAnnotationNameAttribute,
+ InsertDefaultResourceAnnotationToResourcesAnnotation,
RemoveAllParameters,
RemoveAnnotationPreDestroy,
RemoveAnnotationPostConstruct,
diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ModifyAnnotationProposal.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ModifyAnnotationProposal.java
index 8d8b40dd..2ca8ae07 100644
--- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ModifyAnnotationProposal.java
+++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ModifyAnnotationProposal.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2021, 2022 IBM Corporation and others.
+ * Copyright (c) 2021, 2022, 2024 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -19,19 +19,27 @@
import java.util.List;
import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IMemberValuePair;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
+import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.NormalAnnotation;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.StringLiteral;
+import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.TypeLiteral;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
@@ -81,6 +89,7 @@ public ModifyAnnotationProposal(String label, ICompilationUnit targetCU, Compila
@SuppressWarnings("unchecked")
@Override
protected ASTRewrite getRewrite() throws CoreException {
+
CompilationUnit fInvocationNode = getInvocationNode();
IBinding fBinding = getBinding();
String[] annotations = getAnnotations();
@@ -105,11 +114,13 @@ protected ASTRewrite getRewrite() throws CoreException {
boolean isField = declNode instanceof VariableDeclarationFragment;
boolean isSingleVarDecl = declNode instanceof SingleVariableDeclaration;
+ boolean isSingleMemberAnnotation = declNode instanceof SingleMemberAnnotation;
if (isField) {
declNode = declNode.getParent();
}
+ // if the Annotation is declared on a class field
if (declNode.getNodeType() == ASTNode.FIELD_DECLARATION) {
AST ast = declNode.getAST();
ASTRewrite rewrite = ASTRewrite.create(ast);
@@ -144,46 +155,10 @@ protected ASTRewrite getRewrite() throws CoreException {
// add new annotations to proposal (restoring those that were removed)
for (Annotation a : existingAnnotations) {
if (a instanceof NormalAnnotation) {
- NormalAnnotation marker = ast.newNormalAnnotation();
- marker.setTypeName(
- ast.newName(imports.addImport(a.getTypeName().toString(), importRewriteContext)));
- List values = marker.values();
-
- // add existing attributes to annotation
- List existingValues = ((NormalAnnotation) a).values();
- for (MemberValuePair mvp : existingValues) {
- boolean removeAttribute = this.attributesToRemove.contains(mvp.getName().getFullyQualifiedName());
-
- // do not add attributes to be removed
- if (!removeAttribute) {
- MemberValuePair memberValuePair = ast.newMemberValuePair();
- memberValuePair.setName(ast.newSimpleName(mvp.getName().getFullyQualifiedName()));
- StringLiteral stringValue = ast.newStringLiteral();
-
- if (mvp.getValue() instanceof StringLiteral) {
- StringLiteral stringLiteral = (StringLiteral) mvp.getValue();
- stringValue.setLiteralValue(stringLiteral.getLiteralValue());
- } else {
- stringValue.setLiteralValue("");
- }
- memberValuePair.setValue(stringValue);
- values.add(memberValuePair);
- }
- }
-
- // add new attributes
- for (String newAttr : this.attributesToAdd) {
- // dont add duplicate attributes to an annotation
- if (values.stream().noneMatch(v -> v.getName().toString().equals(newAttr))) {
- MemberValuePair memberValuePair = ast.newMemberValuePair();
- memberValuePair.setName(ast.newSimpleName(newAttr));
- StringLiteral stringValue = ast.newStringLiteral();
- stringValue.setLiteralValue("");
- memberValuePair.setValue(stringValue);
- values.add(memberValuePair);
- }
- }
+ NormalAnnotation marker = null;
+ marker = processNormalAnnotation(ast, imports, importRewriteContext, annotations, (NormalAnnotation) a);
+ // add new annotation proposal to the rewrite text edit
rewrite.getListRewrite(declNode,
isField ? FieldDeclaration.MODIFIERS2_PROPERTY : TypeDeclaration.MODIFIERS2_PROPERTY).insertFirst(marker, null);
}
@@ -191,6 +166,7 @@ protected ASTRewrite getRewrite() throws CoreException {
return rewrite;
} else if (declNode instanceof TypeDeclaration || isField || isSingleVarDecl) {
+ // Annotation in question is set on a class declaration or is a method parameter declaration
AST ast = declNode.getAST();
ASTRewrite rewrite = ASTRewrite.create(ast);
@@ -199,59 +175,41 @@ protected ASTRewrite getRewrite() throws CoreException {
ChildListPropertyDescriptor property = isSingleVarDecl ? SingleVariableDeclaration.MODIFIERS2_PROPERTY : TypeDeclaration.MODIFIERS2_PROPERTY;
List extends ASTNode> children = (List extends ASTNode>) declNode.getStructuralProperty(property);
+ boolean isCompositeAnnotation = false;
+
// find and save existing annotation, then remove it from ast
+ // this will cause the entire annotation to be deleted from the file
for (ASTNode child : children) {
+ // for all existing annotations (that are the annotation we want)
if (child instanceof Annotation) {
Annotation annotation = (Annotation) child;
boolean containsAnnotation = Arrays.stream(annotationShortNames).anyMatch(annotation.getTypeName().toString()::contains);
if (containsAnnotation) {
+ // save the existing annotation for processing but remove it from the file
existingAnnotations.add(annotation);
rewrite.remove(child, null);
}
}
}
- // add new annotation with fields from existing annotation
+ // add a new annotation with fields from existing annotation
for (String annotation : annotations) {
- NormalAnnotation marker = ast.newNormalAnnotation();
- marker.setTypeName(ast.newName(imports.addImport(annotation, importRewriteContext)));
- List values = marker.values();
-
+ Annotation newAnnotationToWrite = null;
if (!existingAnnotations.isEmpty()) {
for (Annotation a : existingAnnotations) {
- if (a instanceof NormalAnnotation) {
- List existingValues = ((NormalAnnotation) a).values();
- for (MemberValuePair mvp : existingValues) {
- boolean removeAttribute = this.attributesToRemove.contains(mvp.getName().getFullyQualifiedName());
-
- // do not add attribute to be removed
- if (!removeAttribute) {
- MemberValuePair memberValuePair = ast.newMemberValuePair();
- memberValuePair.setName(ast.newSimpleName(mvp.getName().getFullyQualifiedName()));
- StringLiteral stringValue = ast.newStringLiteral();
-
- if (mvp.getValue() instanceof StringLiteral) {
- StringLiteral stringLiteral = (StringLiteral) mvp.getValue();
- stringValue.setLiteralValue(stringLiteral.getLiteralValue());
- } else {
- stringValue.setLiteralValue("");
- }
- memberValuePair.setValue(stringValue);
- values.add(memberValuePair);
- }
- }
+ if (a instanceof SingleMemberAnnotation) {
+ // this type of annotation contains a single value which may be a list of other annotations,
+ // some of which need to be fixed
+ newAnnotationToWrite = processSingleMemberAnnotation(ast, imports, importRewriteContext, annotations, (SingleMemberAnnotation) a);
+ } else if (a instanceof NormalAnnotation) {
+ // this type of annotation is a base annotation containing a name value pair that needs to be fixed
+ newAnnotationToWrite = processNormalAnnotation(ast, imports, importRewriteContext, annotations, (NormalAnnotation) a);
+
}
}
- }
-
- // add new String attributes
- for (String newAttr : this.attributesToAdd) {
- MemberValuePair memberValuePair = ast.newMemberValuePair();
- memberValuePair.setName(ast.newSimpleName(newAttr));
- StringLiteral stringValue = ast.newStringLiteral();
- stringValue.setLiteralValue("");
- memberValuePair.setValue(stringValue);
- values.add(memberValuePair);
+ } else {
+ // if if this annotation proposal is to add an annotation where one did not exist prior
+ newAnnotationToWrite = createNewAnnotation(ast, imports, importRewriteContext, annotation);
}
ChildListPropertyDescriptor newRewrite;
@@ -263,10 +221,200 @@ protected ASTRewrite getRewrite() throws CoreException {
newRewrite = TypeDeclaration.MODIFIERS2_PROPERTY;
}
- rewrite.getListRewrite(declNode, newRewrite).insertFirst(marker, null);
+ // add new annotation proposal to the rewrite text edit
+ rewrite.getListRewrite(declNode, newRewrite).insertFirst(newAnnotationToWrite, null);
}
return rewrite;
}
return null;
}
+
+ private SingleMemberAnnotation processSingleMemberAnnotation(AST ast, ImportRewrite imports,
+ ImportRewriteContext importRWCtx, String[] annotations,
+ SingleMemberAnnotation annotationToProcess) {
+
+ // A SingleMemberAnnotation is an annotation that contains within it a single
+ // value (with no name associated with it)
+ // That value may be a list of additional annotations, IE:
+ //
+ // @Resources ({ @Resource(name = "aaa"), @Resource(type = Object.class) })
+ //
+ // This method will process the annotations in the list, one by one, for quick
+ // fix actions on any applicable sub annotations
+
+ // Create a new SingleMemebrAnnotation Object that will be used to store the
+ // updates
+ // and used ny the TextEdit to write them to the file
+ SingleMemberAnnotation newSingleMemberAnnotation = ast.newSingleMemberAnnotation();
+
+ // Internally the SingleMemberAnnotation maintains an ArrayInitilaizer which
+ // wraps a List
+ // of NormalAnnotations - create a new empty ArrayInitializer within the new
+ // SingleMemberAnnotation
+ ArrayInitializer newAIInstance = (ArrayInitializer) ast.createInstance(ArrayInitializer.class);
+
+ newSingleMemberAnnotation.setTypeName(
+ ast.newName(imports.addImport(annotationToProcess.getTypeName().toString(), importRWCtx)));
+
+ // Get the empty new List of NormalAnnotations from the new
+ // SingleMemberAnnotation object. This List will hold processed NormalAnnotations from the
+ // original SingleMemberAnnotation object passed into this method above.
+ List newCompositeAnnotationContents = newAIInstance.expressions();
+
+ // An ArrayInitializer 'ai' is the object that holds the list of sub annotations
+ // within the original SingleMemberAnnotation
+ ArrayInitializer ai = (ArrayInitializer) ((SingleMemberAnnotation) annotationToProcess).getValue();
+
+ // get the List of existing NormalAnnotations to process - the 'expressions()' method
+ // returns List
+ List normalAnnotations = ai.expressions();
+
+ if (normalAnnotations.isEmpty()) {
+ // We are fixing an invalid annotation of the form:
+ // @Resources ({}) -
+ // add a single default sub-annotation
+ NormalAnnotation newChildDefaultAnnotation = processNormalAnnotation(ast, imports, importRWCtx, annotations,
+ null);
+ newCompositeAnnotationContents.add(newChildDefaultAnnotation);
+ } else {
+
+ // for each original annotation in the list, process it for the quick fix edit
+ for (NormalAnnotation na : normalAnnotations) {
+ // processNormalAnnotation will create a new NormalAnnotation containing the
+ // results of the quick fix
+ NormalAnnotation newNormalAnnotation = processNormalAnnotation(ast, imports, importRWCtx, annotations,
+ na);
+ // add this new updated NormalAnnotation directly to the new List of
+ // NormalAnnotations
+ newCompositeAnnotationContents.add(newNormalAnnotation);
+ }
+ }
+
+ // now add all of the processed annotations to the new SingleMemberAnnotation to
+ // be written into the file
+ // The ArrayInitializer newAIInstance contains the Lst of NormalAnnotations via
+ // the
+ // newCompositeAnnotationContents.add(newNormalAnnotation);
call in
+ // the previous for loop
+ newSingleMemberAnnotation.setValue(newAIInstance);
+
+ return newSingleMemberAnnotation;
+ }
+
+ private NormalAnnotation processNormalAnnotation(AST ast, ImportRewrite imports, ImportRewriteContext importRWCtx,
+ String[] annotations, NormalAnnotation annotationToProcess) {
+
+ // A NormalAnnotation is an annotation that contains within it a name value pair
+ // IE:
+ //
+ // @Resource(name = "aaa", type = Object.class)
+ //
+ // 'name = "aaa"' is a MemberValuePair of the NormalAnnotation as is 'type = Object.class'
+ //
+ // This method will process this annotation in the list for quick
+ // fix actions on any applicable NormalAnnotation that is passed into this method
+ NormalAnnotation newNormalAnnotation = ast.newNormalAnnotation();
+
+ // for every annotation type we are fixing
+ for (String annotation : annotations) {
+
+ // create a new NormalAnnotation to be written back to the file
+ newNormalAnnotation.setTypeName(ast.newName(imports.addImport(annotation, importRWCtx)));
+ List values = newNormalAnnotation.values();
+
+ if (annotationToProcess == null) {
+ // We are adding a new required default @Resource annotation to an empty
+ // @Resources annotation.
+ addNewAttributes(ast, values);
+ } else {
+ // get the existing name/value pairs from the existing NormalAnnotation that was
+ // passed into this method above
+ List existingValues = ((NormalAnnotation) annotationToProcess).values();
+ for (MemberValuePair mvp : existingValues) {
+ // does the current existing mvp contain the attribute that needs to be added by
+ // this quickfix?
+ boolean containsAttributeToAdd = this.attributesToAdd.contains(mvp.getName().getFullyQualifiedName());
+ // does the current existing mvp contain all the attributes that need to be
+ // added by this quickfix?
+ boolean containsAllToAdd = this.attributesToAdd.stream().allMatch(attr -> existingValues.stream().anyMatch(v -> v.equals(attr)));
+ // does the current existing mvp contain an attribute that is due to be removed
+ // by this quickfix?
+ boolean removeAttribute = this.attributesToRemove.contains(mvp.getName().getFullyQualifiedName());
+
+ if (!containsAttributeToAdd || !containsAllToAdd) {
+ // the existing NormalAnnotation currently being processed does not contain the
+ // attribute to be added by this quickfix
+ // so the quickfix should be applied to it.
+ // But the current existing MVP entry within the NormalAnnotation is valid and
+ // should continue to exist.
+ // Copy over any existing valid mvp pairs into a new MVP that will be written
+ // back to the file as part of this quickfix action
+
+ // (but) do not add an existing mvp that is to be removed by this quick fix
+ if (!removeAttribute) {
+ // create a new MVP to hold the existing mvp
+ MemberValuePair newMemberValuePair = ast.newMemberValuePair();
+ // copy the existing name portion of the MVP into the new MVP
+ newMemberValuePair.setName(ast.newSimpleName(mvp.getName().getFullyQualifiedName()));
+
+ // copy the existing value into the new MVP, depending on what type it is
+ if (mvp.getValue() instanceof StringLiteral) {
+ newMemberValuePair.setValue((StringLiteral) mvp.getValue().copySubtree(ast, mvp.getValue()));
+ } else if (mvp.getValue() instanceof TypeLiteral) {
+ newMemberValuePair.setValue((TypeLiteral) mvp.getValue().copySubtree(ast, mvp.getValue()));
+
+ }
+
+ // add this new MVP into the new NormalAnnotation
+ values.add(newMemberValuePair);
+ }
+ } else {
+ // the current NormalAnnotation being processed already contains the attribute
+ // to be added by this quick fix action
+ // and so is not the one to have the quickfix applied to it
+ // return an as-is copy of the existing annotation
+ newNormalAnnotation = (NormalAnnotation) annotationToProcess.copySubtree(ast, annotationToProcess);
+ return newNormalAnnotation;
+ }
+
+ }
+
+ // now add the attribute for this quickfix action to the new NormalAnnotation
+ values = addNewAttributes(ast, values);
+ }
+ }
+ return newNormalAnnotation;
+ }
+
+ private NormalAnnotation createNewAnnotation(AST ast, ImportRewrite imports, ImportRewriteContext importRWCtx,
+ String annotation) {
+
+ NormalAnnotation marker = ast.newNormalAnnotation();
+
+ marker.setTypeName(ast.newName(imports.addImport(annotation, importRWCtx)));
+ List values = marker.values();
+
+ values = addNewAttributes(ast, values);
+
+ return marker;
+
+ }
+
+ private List addNewAttributes(AST ast, List values) {
+ // add new String attributes
+ // we are adding empty strings for values because we cannot know what the user
+ // wishes to have - they will have to add that themselves
+ // ie: name="" or type=""
+ for (String newAttr : this.attributesToAdd) {
+ if (values.stream().noneMatch(v -> v.getName().toString().equals(newAttr))) {
+ MemberValuePair newMemberValuePair = ast.newMemberValuePair();
+ newMemberValuePair.setName(ast.newSimpleName(newAttr));
+ StringLiteral stringValue = ast.newStringLiteral();
+ stringValue.setLiteralValue("");
+ newMemberValuePair.setValue(stringValue);
+ values.add(newMemberValuePair);
+ }
+ }
+ return values;
+ }
}
diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/AnnotationDiagnosticsParticipant.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/AnnotationDiagnosticsParticipant.java
index 368656fb..6d712b72 100644
--- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/AnnotationDiagnosticsParticipant.java
+++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/AnnotationDiagnosticsParticipant.java
@@ -1,5 +1,5 @@
/*******************************************************************************
-* Copyright (c) 2021, 2023 IBM Corporation and others.
+* Copyright (c) 2021, 2023, 2024 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -65,7 +65,8 @@ public List collectDiagnostics(JavaDiagnosticsContext context, IProg
ArrayList> annotatables = new ArrayList>();
String[] validAnnotations = { Constants.GENERATED_FQ_NAME };
String[] validTypeAnnotations = { Constants.GENERATED_FQ_NAME,
- Constants.RESOURCE_FQ_NAME };
+ Constants.RESOURCE_FQ_NAME,
+ Constants.RESOURCES_FQ_NAME };
String[] validMethodAnnotations = { Constants.GENERATED_FQ_NAME,
Constants.POST_CONSTRUCT_FQ_NAME, Constants.PRE_DESTROY_FQ_NAME,
Constants.RESOURCE_FQ_NAME };
@@ -120,6 +121,7 @@ public List collectDiagnostics(JavaDiagnosticsContext context, IProg
IAnnotation annotation = annotatable.getFirst();
IAnnotatable element = annotatable.getSecond();
+ // process Types? (class declarations)
if (DiagnosticUtils.isMatchedAnnotation(unit, annotation, Constants.GENERATED_FQ_NAME)) {
for (IMemberValuePair pair : annotation.getMemberValuePairs()) {
// If date element exists and is non-empty, it must follow ISO 8601 format.
@@ -178,7 +180,74 @@ public List collectDiagnostics(JavaDiagnosticsContext context, IProg
}
}
}
+ } else if (DiagnosticUtils.isMatchedAnnotation(unit, annotation, Constants.RESOURCES_FQ_NAME)) {
+ if (element instanceof IType) {
+ for (IMemberValuePair internalAnnotation : annotation.getMemberValuePairs()) {
+ Object[] valuePairs = (Object[]) internalAnnotation.getValue();
+ String diagnosticMessage;
+ Range annotationRange = null;
+ if (valuePairs.length == 0) {
+ annotationRange = PositionUtils.toNameRange(annotation, context.getUtils());
+ diagnosticMessage = Messages.getMessage("ResourcesAnnotationMustDefineResourceAnnotation",
+ "@Resources", "@Resource");
+ diagnostics.add(context.createDiagnostic(uri, diagnosticMessage, annotationRange,
+ Constants.DIAGNOSTIC_SOURCE,
+ ErrorCode.MissingResourceAnnotation,
+ DiagnosticSeverity.Error));
+ }
+ int objKind = internalAnnotation.getValueKind();
+ for (Object childAnnotationObj : valuePairs) {
+ if (objKind == IMemberValuePair.K_ANNOTATION) {
+ IAnnotation childAnnotation = (IAnnotation) childAnnotationObj;
+ if (DiagnosticUtils.isMatchedAnnotation(unit, childAnnotation,
+ Constants.RESOURCE_FQ_NAME)) {
+ if (element instanceof IType) {
+ IType type = (IType) element;
+ if (type.getElementType() == IJavaElement.TYPE
+ && ((IType) type).isClass()) {
+ annotationRange = PositionUtils.toNameRange(childAnnotation,
+ context.getUtils());
+ Boolean nameEmpty = true;
+ Boolean typeEmpty = true;
+ for (IMemberValuePair pair : childAnnotation.getMemberValuePairs()) {
+ if (pair.getMemberName().equals("name")) {
+ nameEmpty = false;
+ }
+ if (pair.getMemberName().equals("type")) {
+ typeEmpty = false;
+ }
+ }
+ if (nameEmpty) {
+ diagnosticMessage = Messages.getMessage(
+ "AnnotationMustDefineAttribute",
+ "@Resource", "name");
+ diagnostics.add(context.createDiagnostic(uri, diagnosticMessage,
+ annotationRange,
+ Constants.DIAGNOSTIC_SOURCE,
+ ErrorCode.MissingResourceNameAttribute,
+ DiagnosticSeverity.Error));
+ }
+
+ if (typeEmpty) {
+ diagnosticMessage = Messages.getMessage(
+ "AnnotationMustDefineAttribute",
+ "@Resource", "type");
+ diagnostics.add(context.createDiagnostic(uri, diagnosticMessage,
+ annotationRange,
+ Constants.DIAGNOSTIC_SOURCE,
+ ErrorCode.MissingResourceTypeAttribute,
+ DiagnosticSeverity.Error));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
+
+ // process methods now?
if (DiagnosticUtils.isMatchedAnnotation(unit, annotation, Constants.POST_CONSTRUCT_FQ_NAME)) {
if (element instanceof IMethod) {
IMethod method = (IMethod) element;
diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/Constants.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/Constants.java
index d1b740d7..e66c6a4e 100644
--- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/Constants.java
+++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/Constants.java
@@ -1,5 +1,5 @@
/*******************************************************************************
-* Copyright (c) 2021, 2023 IBM Corporation and others.
+* Copyright (c) 2021, 2023, 2024 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -30,6 +30,10 @@ public class Constants {
public static final String RESOURCE = "Resource";
public static final String RESOURCE_FQ_NAME = "jakarta.annotation.Resource";
+ /* @Resources */
+ public static final String RESOURCES = "Resources";
+ public static final String RESOURCES_FQ_NAME = "jakarta.annotation.Resources";
+
/* @PostConstruct */
public static final String POST_CONSTRUCT = "PostConstruct";
public static final String POST_CONSTRUCT_FQ_NAME = "jakarta.annotation.PostConstruct";
diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/ErrorCode.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/ErrorCode.java
index 9c8406b7..f35dc602 100644
--- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/ErrorCode.java
+++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/ErrorCode.java
@@ -1,5 +1,5 @@
/*******************************************************************************
-* Copyright (c) 2023 IBM Corporation and others.
+* Copyright (c) 2023, 2024 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -19,6 +19,7 @@
*/
public enum ErrorCode implements IJavaErrorCode {
InvalidDateFormat,
+ MissingResourceAnnotation,
MissingResourceNameAttribute,
MissingResourceTypeAttribute,
PostConstructParams,
diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/InsertDefaultResourceAnnotationToResourcesAnnotation.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/InsertDefaultResourceAnnotationToResourcesAnnotation.java
new file mode 100644
index 00000000..f7a1eb5e
--- /dev/null
+++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/InsertDefaultResourceAnnotationToResourcesAnnotation.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+* Copyright (c) 2024 IBM Corporation and others.
+*
+* This program and the accompanying materials are made available under the
+* terms of the Eclipse Public License v. 2.0 which is available at
+* http://www.eclipse.org/legal/epl-2.0.
+*
+* SPDX-License-Identifier: EPL-2.0
+*
+* Contributors:
+* IBM Corporation - initial API and implementation
+*******************************************************************************/
+package org.eclipse.lsp4jakarta.jdt.internal.annotations;
+
+import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId;
+import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId;
+import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.InsertAnnotationAttributesQuickFix;
+
+/**
+ * Inserts the name attribute to the @Resource annotation to the active element.
+ */
+public class InsertDefaultResourceAnnotationToResourcesAnnotation extends InsertAnnotationAttributesQuickFix {
+
+ /**
+ * Constructor.
+ */
+ public InsertDefaultResourceAnnotationToResourcesAnnotation() {
+ super("jakarta.annotation.Resource", "name", "type");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getParticipantId() {
+ return InsertDefaultResourceAnnotationToResourcesAnnotation.class.getName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected ICodeActionId getCodeActionId() {
+ return JakartaCodeActionId.InsertDefaultResourceAnnotationToResourcesAnnotation;
+ }
+}
diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/resources/org/eclipse/lsp4jakarta/jdt/core/messages/messages.properties b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/resources/org/eclipse/lsp4jakarta/jdt/core/messages/messages.properties
index 22cfc6ce..9c434940 100644
--- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/resources/org/eclipse/lsp4jakarta/jdt/core/messages/messages.properties
+++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/resources/org/eclipse/lsp4jakarta/jdt/core/messages/messages.properties
@@ -18,6 +18,7 @@ MethodMustNotHaveParameters = A method with the {0} annotation must not have any
MethodMustBeVoid = A method with the {0} annotation must be void.
MethodMustNotBeStatic = A method with the {0} annotation must not be static.
MethodMustNotThrow = A method with the {0} annotation must not throw checked exceptions.
+ResourcesAnnotationMustDefineResourceAnnotation = The {0} annotation must define at least one sub-annotation ''{1}''.
# HttpServletQuickFix
LetClassExtend = Let ''{0}'' extend ''{1}''
diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/annotations/ResourceAnnotation.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/annotations/ResourceAnnotation.java
index 967a5892..0f429e11 100644
--- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/annotations/ResourceAnnotation.java
+++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/annotations/ResourceAnnotation.java
@@ -1,23 +1,23 @@
package io.openliberty.sample.jakarta.annotations;
import jakarta.annotation.Resource;
+import jakarta.annotation.Resources;
@Resource(type = Object.class, name = "aa")
public class ResourceAnnotation {
private Integer studentId;
-
@Resource(shareable = true)
private boolean isHappy;
@Resource(name = "test")
private boolean isSad;
-
private String emailAddress;
-
-
+
+
+
}
@Resource(name = "aa")
diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/annotations/ResourcesAnnotation.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/annotations/ResourcesAnnotation.java
new file mode 100644
index 00000000..3aaf2c5a
--- /dev/null
+++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/annotations/ResourcesAnnotation.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+* Copyright (c) 2024 IBM Corporation and others.
+*
+* This program and the accompanying materials are made available under the
+* terms of the Eclipse Public License v. 2.0 which is available at
+* http://www.eclipse.org/legal/epl-2.0.
+*
+* SPDX-License-Identifier: EPL-2.0
+*
+* Contributors:
+* IBM Corporation - initial API and implementation
+*******************************************************************************/
+package io.openliberty.sample.jakarta.annotations;
+
+import jakarta.annotation.Resource;
+import jakarta.annotation.Resources;
+
+@Resources ({ @Resource(name = "aaa"), @Resource(type = Object.class) })
+public class ResourcesAnnotation {
+}
+
+@Resources ({})
+class ResourcesAnnotationEmpty {
+}
+
+@Resource(name = "aa", type = Object.class)
+class DoctoralStudent {
+}
\ No newline at end of file
diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/ResourceAnnotationTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/ResourceAnnotationTest.java
index 40f8fec8..33a39f90 100644
--- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/ResourceAnnotationTest.java
+++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/ResourceAnnotationTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
-* Copyright (c) 2021, 2022 IBM Corporation and others.
+* Copyright (c) 2021, 2022, 2024 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -63,10 +63,10 @@ public void GeneratedAnnotation() throws Exception {
assertJavaCodeAction(codeActionParams, IJDT_UTILS, ca);
JakartaJavaCodeActionParams codeActionParams1 = createCodeActionParams(uri, d2);
- TextEdit te1 = te(39, 0, 39, 30, "@Resource(type = \"\", name = \"\")");
+ TextEdit te1 = te(39, 0, 39, 30, "@Resource(type = Object.class, name = \"\")");
CodeAction ca1 = ca(uri, "Insert 'name' attribute to @Resource", d2, te1);
assertJavaCodeAction(codeActionParams1, IJDT_UTILS, ca1);
}
-}
+}
\ No newline at end of file
diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/ResourcesAnnotationTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/ResourcesAnnotationTest.java
new file mode 100644
index 00000000..bf48c3c7
--- /dev/null
+++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/ResourcesAnnotationTest.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+* Copyright (c) 2024 IBM Corporation and others.
+*
+* This program and the accompanying materials are made available under the
+* terms of the Eclipse Public License v. 2.0 which is available at
+* http://www.eclipse.org/legal/epl-2.0.
+*
+* SPDX-License-Identifier: EPL-2.0
+*
+* Contributors:
+* IBM Corporation - initial API and implementation
+*******************************************************************************/
+package org.eclipse.lsp4jakarta.jdt.annotations;
+
+import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.assertJavaCodeAction;
+import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.assertJavaDiagnostics;
+import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.ca;
+import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.createCodeActionParams;
+import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.d;
+import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.te;
+
+import java.util.Arrays;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.lsp4j.CodeAction;
+import org.eclipse.lsp4j.Diagnostic;
+import org.eclipse.lsp4j.DiagnosticSeverity;
+import org.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams;
+import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams;
+import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest;
+import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils;
+import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl;
+import org.junit.Test;
+
+public class ResourcesAnnotationTest extends BaseJakartaTest {
+
+ protected static IJDTUtils IJDT_UTILS = JDTUtilsLSImpl.getInstance();
+
+ @Test
+ public void GeneratedAnnotation() throws Exception {
+ IJavaProject javaProject = loadJavaProject("jakarta-sample", "");
+ IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/annotations/ResourcesAnnotation.java"));
+ String uri = javaFile.getLocation().toFile().toURI().toString();
+
+ JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams();
+ diagnosticsParams.setUris(Arrays.asList(uri));
+
+ // expected annotations
+ Diagnostic d1 = d(17, 14, 37, "The @Resource annotation must define the attribute 'type'.",
+ DiagnosticSeverity.Error, "jakarta-annotations", "MissingResourceTypeAttribute");
+
+ Diagnostic d2 = d(17, 39, 69, "The @Resource annotation must define the attribute 'name'.",
+ DiagnosticSeverity.Error, "jakarta-annotations", "MissingResourceNameAttribute");
+
+ Diagnostic d3 = d(21, 0, 15, "The @Resources annotation must define at least one sub-annotation '@Resource'.",
+ DiagnosticSeverity.Error, "jakarta-annotations", "MissingResourceAnnotation");
+
+ assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2, d3);
+
+ JakartaJavaCodeActionParams codeActionParams = createCodeActionParams(uri, d1);
+ TextEdit te1 = te(17, 0, 18, 0, "@Resources({ @Resource(name = \"aaa\", type = \"\"), @Resource(type = Object.class) })\n");
+ CodeAction ca1 = ca(uri, "Insert 'type' attribute to @Resource", d1, te1);
+ assertJavaCodeAction(codeActionParams, IJDT_UTILS, ca1);
+
+ JakartaJavaCodeActionParams codeActionParams1 = createCodeActionParams(uri, d2);
+ TextEdit te2 = te(17, 0, 18, 0, "@Resources({ @Resource(name = \"aaa\"), @Resource(type = Object.class, name = \"\") })\n");
+ CodeAction ca2 = ca(uri, "Insert 'name' attribute to @Resource", d2, te2);
+ assertJavaCodeAction(codeActionParams1, IJDT_UTILS, ca2);
+
+ JakartaJavaCodeActionParams codeActionParams2 = createCodeActionParams(uri, d3);
+ TextEdit te3 = te(21, 0, 21, 15, "@Resources({ @Resource(name = \"\", type = \"\") })");
+ CodeAction ca3 = ca(uri, "Insert 'name,type' attributes to @Resource", d3, te3);
+ assertJavaCodeAction(codeActionParams2, IJDT_UTILS, ca3);
+ }
+}