jaykataria1111 commented on issue #2769:
URL: 
https://github.com/apache/logging-log4j2/issues/2769#issuecomment-2458996673

   Hi @ppkarwasz 
   
   Made some good progress but needed your input on something:
   
   I am not sure if we should add type checking here: 
   
   ```
   if (methodName.startsWith("set")
                           && methodElement.getParameters().size() == 1) {
                       // Check if this is the right setter and if it matches 
return
                   }
   ```
   
   I added this code: 
   ```
    if ((methodName.toLowerCase(Locale.ROOT).startsWith("set") // Typical 
pattern for setters
                               || 
methodName.toLowerCase(Locale.ROOT).startsWith("with")) // Typical pattern for 
setters
                               && methodElement.getParameters().size() == 1 // 
It is a weird pattern to not have public setter
                       ) {
   
                           // Check if the method parameter matches the type.
                           boolean methodParamTypeMatchesBoxedFieldType = false;
                           TypeMirror fieldType = element.asType();
                           TypeMirror methodParamType = 
methodElement.getParameters().get(0).asType();
                           Types typeUtils = processingEnv.getTypeUtils();
   
                           if (fieldType.getKind().isPrimitive() && 
!methodParamType.getKind().isPrimitive()) {
                               methodParamTypeMatchesBoxedFieldType = 
typeUtils.isSameType(methodParamType, typeUtils.boxedClass((PrimitiveType) 
fieldType).asType());
                           } else if (methodParamType.getKind().isPrimitive() 
&& !fieldType.getKind().isPrimitive()) {
                               methodParamTypeMatchesBoxedFieldType = 
typeUtils.isSameType(fieldType, typeUtils.boxedClass((PrimitiveType) 
methodParamType).asType());
                           }
                           boolean methodParamTypeMatchesFieldType = 
typeUtils.isSameType(methodParamType, fieldType) || 
methodParamTypeMatchesBoxedFieldType;
   
                           // Check if method is public
                           boolean isPublicMethod = 
methodElement.getModifiers().contains(Modifier.PUBLIC);
   
                         boolean foundPublicSetter = 
methodParamTypeMatchesFieldType && isPublicMethod;
                         if (foundPublicSetter) {
                             // Hurray we found a public setter for the field!
                             return;
                         }
                       }
                        
   ```
   
   But the issue that I face is with such kind of setters, we are throwing an 
error in such cases due to type checking : 
   
   ```
    public static final class Builder implements 
org.apache.logging.log4j.core.util.Builder<IfLastModified> {
           @PluginBuilderAttribute
           @Required(message = "No age provided for IfLastModified")
           private 
org.apache.logging.log4j.core.appender.rolling.action.Duration age;
   
           public Builder setAge(final Duration age) {
               this.age = 
org.apache.logging.log4j.core.appender.rolling.action.Duration.ofMillis(age.toMillis());
               return this;
           }
   ```
   
   As you can see the setAge method has a different parameter type than the 
fields type. 
   
   if we do have a hard check on the naming convention "setFieldName" (This 
becomes more restrictive, do not recommend, I have seen people get really 
creative for no reason) that has  a number of problems as well. 
   
   I do believe we should do some sort of checks, just checking for 
`methodName.startsWith("set") && methodElement.getParameters().size() == 1` to 
identify 
   
   Hence I have come up with the following plan: 
   1. method name starts with "set" or "with" and method has 1 param
   2. method param type is the same as the field type (includes boxed 
comparisons)
   2.a if the above is not being followed then as a fall back we can check if 
there is a method that follows the naming convention like in the above case
   
   after that I believe we can give up, because then people are doing something 
really creative with their code.
   
   Hence building on the above statement, I have the following logic, which 
seems to work :) : 
   
   ```
      if ((methodName.toLowerCase(Locale.ROOT).startsWith("set") // Typical 
pattern for setters
                               || 
methodName.toLowerCase(Locale.ROOT).startsWith("with")) // Typical pattern for 
setters
                               && methodElement.getParameters().size() == 1 // 
It is a weird pattern to not have public setter
                       ) {
   
                           // Check if the method parameter matches the type.
                           boolean methodParamTypeMatchesBoxedFieldType = false;
                           TypeMirror fieldType = element.asType();
                           TypeMirror methodParamType = 
methodElement.getParameters().get(0).asType();
                           Types typeUtils = processingEnv.getTypeUtils();
   
                           if (fieldType.getKind().isPrimitive() && 
!methodParamType.getKind().isPrimitive()) {
                               methodParamTypeMatchesBoxedFieldType = 
typeUtils.isSameType(methodParamType, typeUtils.boxedClass((PrimitiveType) 
fieldType).asType());
                           } else if (methodParamType.getKind().isPrimitive() 
&& !fieldType.getKind().isPrimitive()) {
                               methodParamTypeMatchesBoxedFieldType = 
typeUtils.isSameType(fieldType, typeUtils.boxedClass((PrimitiveType) 
methodParamType).asType());
                           }
                           boolean methodParamTypeMatchesFieldType = 
typeUtils.isSameType(methodParamType, fieldType) || 
methodParamTypeMatchesBoxedFieldType;
   
                           boolean containsName = 
methodName.toLowerCase(Locale.ROOT).equals(String.format("set%s",fieldName.toLowerCase(Locale.ROOT)))
                                   || 
methodName.toLowerCase(Locale.ROOT).equals(String.format("with%s",fieldName.toLowerCase(Locale.ROOT)));
                           // Check if method is public
                           boolean isPublicMethod = 
methodElement.getModifiers().contains(Modifier.PUBLIC);
   
                         boolean foundPublicSetter = 
(methodParamTypeMatchesFieldType || containsName) && isPublicMethod;
                         if (foundPublicSetter) {
                             // Hurray we found a public setter for the field!
                             return;
                         }
                       }
                   }
               }
   ```
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to