Skip to content

Commit

Permalink
Allow for inspection of annotations within annotations
Browse files Browse the repository at this point in the history
Refactor and cleanup

Signed-off-by: Juan Manuel Leflet Estrada <jleflete@redhat.com>
  • Loading branch information
jmle committed Aug 22, 2024
1 parent 12c6b52 commit dd160b3
Showing 1 changed file with 102 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,33 +50,25 @@ default boolean matchesAnnotationQuery(SearchMatch match, List<Class<? extends S
// See if the annotation's name matches the pattern given in the query for the annotation
String fqn = getFQN(annotation);
if (getAnnotationQuery().matchesAnnotation(fqn)) {
// If the query has annotation elements to check, iterate through the annotation's values and check
if (getAnnotationQuery().getElements() != null && !getAnnotationQuery().getElements().entrySet().isEmpty()) {
IMemberValuePair[] memberValuePairs = annotation.getMemberValuePairs();
Set<Map.Entry<String, String>> annotationElements = getAnnotationQuery().getElements().entrySet();
for (IMemberValuePair member : memberValuePairs) {
for (Map.Entry<String, String> annotationElement : annotationElements) {
if (annotationElement.getKey().equals(member.getMemberName())) {
// Member values can be arrays. In this case, lets iterate over it and compare:
if (member.getValue() instanceof Object[]) {
Object[] values = (Object[]) member.getValue();
// TODO: at the moment we are just toString()ing the values.
// We might want to make this more sophisticated, relying on
// member.getValueKind() to match on specific kinds.
return Arrays.stream(values).anyMatch(v -> Pattern.matches(annotationElement.getValue(), v.toString()));
} else {
if (Pattern.matches(annotationElement.getValue(), member.getValue().toString())) {
return true;
}
return doElementsMatch((Annotation) annotation);
} else {
// The LS doesn't seem to be able to match on annotations within annotations, but
// if the main annotation doesn't match, there might be some annotations inside:
for (IMemberValuePair member : annotation.getMemberValuePairs()) {
if (member.getValueKind() == IMemberValuePair.K_ANNOTATION) {
if (member.getValue() instanceof Object[]) {
Object[] objs = (Object[]) member.getValue();
for (int i = 0; i < objs.length; i++) {
Annotation innerAnnotation = (Annotation) objs[i];
fqn = getFQN(innerAnnotation);
if (getAnnotationQuery().matchesAnnotation(fqn)) {
return doElementsMatch(innerAnnotation);
}
}
}

}
} else {
// No annotation elements, but the annotation itself matches
return true;
}

}
}
return false;
Expand All @@ -89,6 +81,94 @@ default boolean matchesAnnotationQuery(SearchMatch match, List<Class<? extends S
return true;
}

/**
* This relatively complicated function checks whether the elements of the annotation query match
* the elements of the actual annotation being inspected.
*
* Java annotations can have all sorts of elements inside: Strings, Arrays, or even other annotations:
* <pre>
* {@code
* @DataSourceDefinitions({
* @DataSourceDefinition(
* name = "jdbc/multiple-ds-xa",
* className="com.example.MyDataSource",
* portNumber=6689,
* serverName="example.com",
* user="lance",
* password="secret"
* ),
* @DataSourceDefinition(
* name = "jdbc/multiple-ds-non-xa",
* className="com.example.MyDataSource",
* portNumber=6689,
* serverName="example.com",
* user="lance",
* password="secret",
* transactional = false
* ),
* })
* public class AnnotationMultipleDs {
* }
*
* </pre>
*
* If we have a query like this:
* <pre>
* when:
* java.referenced:
* location: ANNOTATION
* pattern: javax.annotation.sql.DataSourceDefinition
* annotated:
* elements:
* - name: transactional
* value: false
* </pre>
* we need to check if the annotation elements (the different config properties defined within the annotation)
* match the one(s) in the query. The query can define more than one element, as shown by the fact that the
* "elements" node is an array, therefore, if multiple elements are present in the query, all must be matched.
*
* @param annotation the annotation to inspect
* @return a boolean indicating whether the elements match
* @throws JavaModelException
*/
private boolean doElementsMatch(Annotation annotation) throws JavaModelException {
// If the query has annotation elements to check, iterate through the annotation's values and check
if (getAnnotationQuery().getElements() != null && !getAnnotationQuery().getElements().entrySet().isEmpty()) {
IMemberValuePair[] memberValuePairs = annotation.getMemberValuePairs();
Set<Map.Entry<String, String>> ruleAnnotationElems = getAnnotationQuery().getElements().entrySet();
boolean allElementsMatch = true;
boolean oneElementMatched = false;
// TODO: there is a problem with defaults: they don't appear in the memberValuePairs so they cannot be matched
for (int i = 0; i < memberValuePairs.length && allElementsMatch; i++) {
IMemberValuePair member = memberValuePairs[i];
for (Map.Entry<String, String> ruleAnnotationElem : ruleAnnotationElems) {
String ruleAnnotationElementName = ruleAnnotationElem.getKey();
if (ruleAnnotationElementName.equals(member.getMemberName())) {
// Member values can be arrays. In this case, lets iterate over it and compare:
if (member.getValue() instanceof Object[]) {
Object[] values = (Object[]) member.getValue();
// TODO: at the moment we are just toString()ing the values.
// We might want to make this more sophisticated, relying on
// member.getValueKind() to match on specific kinds. This however can match
boolean valueMatches = Arrays.stream(values).anyMatch(v -> Pattern.matches(ruleAnnotationElem.getValue(), v.toString()));
oneElementMatched |= valueMatches;
allElementsMatch &= valueMatches;
} else {
boolean valueMatches = Pattern.matches(ruleAnnotationElem.getValue(), member.getValue().toString());
oneElementMatched |= valueMatches;
allElementsMatch &= valueMatches;
}
}
}
}
return oneElementMatched && allElementsMatch;
}

// No annotation elements, but the annotation itself matches
return true;

}

private IAnnotation[] tryToGetAnnotations(SourceRefElement t) {
try {
return t.getAnnotations();
Expand Down

0 comments on commit dd160b3

Please sign in to comment.