lombok.config property is not stopping compiler/IDE warning - java

I am storing some properties in lombok.config, which, I though, is a great way to reduce unnecessary stacks of lombok annotations on my classes. One of these properties is the following;
lombok.equalsAndHashCode.callSuper = CALL
I though this would shut the compiler up & end the following IDE (Intellij) warnings about putting an #EqualsAndHashCode with callSuper value set on those classes with #Data and extending some other class;
Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is intentional, add #EqualsAndHashCode(callSuper=false) to your type.
It does not though! Is there a way to stop these warnings without adding the callSuper property directly on the classes?

Related

toString method added dynamically to project

Is there any option to add Lombok's annotation #ToString dynamically (f.e. during building the code) to all classes from the custom package, f.e. xxx.yyy.dao.* ?
I've tried with aspect approach:
declare #type : xxx.yyy.dao.* : #lombok.ToString;
but i got
AJC compiler error: org.aspectj.weaver.patterns.DeclareAnnotation -> Index 1 out of bounds for length 1
I guess it is not allowed as lombok's annotations are also loaded kinda at same compilation time.
The goal is to have toString() method applied by default to all classes from the given package (in such case a developer doesn't need to remember to add #ToString manually to each class).
I just noticed that you use a Lombok annotation, but those all have SOURCE retention. It simply does not make any sense to declare a source-level annotation on woven byte code, it is paradoxical. Nevertheless, the AspectJ weaver should be improved to show a proper warning instead of a spurious weaving error.
Actually, this is a known bug since 2011, which I just commented on your behalf:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=366085
In order to solve your problem, you either need to add a source-level preprocessing step to your build which kicks in even before Lombok, or you need to develop some kind of ToStringAspect which generates or intercepts toString methods on the fly, dynamically using reflection to iterate over instance fields and creating a meaningful string representation for them.

Lombok #Wither/#With Inheritance (super-/sub-classes)

Please suggest how to use #Wither/#With when inheritance applied.
I have an abstract class Parent and concrete Child. Child is supposed to be immutable. Putting #Wither on both gives me two errors:
The constructor Child(String) is undefined
The type Child must implement the inherited abstract method
Parent.withA(String)
#Value
#Wither
#NonFinal
#SuperBuilder
abstract class Parent {
String a;
}
#Value
#Wither
#EqualsAndHashCode(callSuper = true)
#SuperBuilder
class Child extends Parent {
String b;
}
I'd be happy to just remove #Wither and use the builder methods, but I'm refactoring a public library (trying to optimize the model classes) and I don't want compilation errors on my clients.
I also found this issue that explains the second error. But the logic of the intention is not clear https://github.com/rzwitserloot/lombok/issues/945
Lombok is an annotation processor. Those run on each compilation unit (i.e. Java file) and do not have access to information from other compilation units. That means that Lombok cannot know anything about the contents of class Parent when processing Child.
So when generating the code for Child, Lombok does not know what wither methods are inherited from Parent. Consequently, it cannot generate an implementation for the abstract withA() from Parent.
A second problem is that the wither methods need a constructor that has all fields as parameter, including those from the superclass. That is also impossible to generate for Lombok due to the aforementioned limitation.
To make a long story short: #Wither does not work well with inheritance. I suggest putting it only on Parent and implementing it manually for Child.
Another option is to put #SuperBuilder(toBuilder=true) on both classes and then use instance.toBuilder().a("newValue").build().

Project Lombok - Value annotation: is it possible to suppress #Getter?

After scouring the Lombok feature-list and in particular the documentation for the Getter/Setter and #Value annotations I have not been able to find any setting that suppresses the code generated by #Getter.
In practice, #Value is shorthand for: final #ToString #EqualsAndHashCode #AllArgsConstructor #FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) #Getter
This is important as I do not want to leak references to objects that are themselves mutable.
Effective Java references this type of issue in "Item 39: Make defensive copies when needed". It seems that #Wither could partly solve this issue by making actual defensive copies but I want to avoid leaking attributes what so ever, regardless of them being mutable.
While it is possible to roll one's own #Value annotation that omits the #Getter I would, of course, prefer not to as that would add unwarranted complexity to the codebase if such a setting already exists.
You could use:
#Value
#Getter(AccessLevel.NONE)
AccessLevel.NONE instructs Lombok to not generate the getters. That's the best you can do right now.

Can I aggregate, proxy, or imitate #SuppressWarnings in Java?

I use Spring to build a ModelMap, which is rendered and then parsed by Mustache. For this reason, I have some classes that only exist to wrap data for Mustache's convenience.
These classes have fields and methods that are never used in the code, but are in fact used by Spring (and, in turn, Mustache). I want to explicitly state that this is intentional by annotating the class with a custom annotation #ForMustache that looks like this:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.SOURCE)
public #interface ForMustache {
}
My goal is basically a fancy way of saying #SuppressWarnings("unused") // For Mustache.
I attempted to annotate ForMustache with #SuppressWarnings("unused"), but its effects apply inside my annotation instead of being propagated to the type annotated with #ForMustache.
Can I proxy or imitate #SuppressWarnings("unused") with another annotation?
The #SuppressWarnings annotation is meant for the compiler and other compile time tools that work directly on the source files.
It is also specified as
#Retention(RetentionPolicy.SOURCE)
public #interface SuppressWarnings { ...
meaning it's not supposed to be available anymore at runtime. This means that even springs meta-annotation aware processing can't see it anymore.
Given enough time, you could build an annotation processor that, during compile-time, transforms a custom #ForMustache annotation into a #SuppressWarnings so the compiler can pick it up. In similar fashion Lombok can turn
#Data
class Foo {
private final String value;
}
into a) code that compiles and doesn't complain about no assignment to a final field and b) it wouldn't generate a warning because it generates a public getter for the field. Those can only be assumed to be used / they are usable from places outside the compiler's reach.
Besides putting #SuppressWarnings everywhere, your only chance is to remove the condition that causes the warning.
Disable the check with a compiler option
add public getters or other code so the fields are used (with lombok for example)
hook into the compile process.

Search for all instances of certain annotation type

Suppose I have this annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Name
{
String value();
}
This is going to be used as follows
#Name("name1")
public static Foo foo = new Foo();
I have multiples of these across my project source files. Is there an fairly simple way to search and collect all those "foo"s that're preceded by #Name?
In other words, I'd like to write a method that would return a Set<Foo> containing these.
Thanks!!!
I am not really familiar with the classpath scanners that others are suggesting. They seem like a robust - if not ideal - solution.
If you have control over the source, then you could use annotation processing.
Create an annotation processor that will create a class - MapClass with a static member Map<String,Foo>. Every time the annotation processor encounters the #Name annotation, it adds that to the source code of MapClass. When it finishes processing the annotations, it will have the same effect as if you hard coded the map.
Annotation processing happens during compile time. If some of the classes in your project are not compiled by you. For example, if someone else compiles some classes and gives a jar to you, then it won't work as easily. But if all the classes are compiled by you then it should not be a problem.
To create an annotation processor, extend AbstractProcessor. You will want to annotate your class with a # SupportedAnnotationTypes ( "Name" ) annotation (make sure name is the fully qualified name of your annotation.
Override the process method. process has two parameters: annotations and roundEnv. annotations is just the set of annotations that this particular processor supports - in your case it should be (Name). roundEnv is a useful utility class.
Iterate through the one annotation in annotations. Use roundEnv to getElementsAnnotatedWith. This should give you the set of all elements that carry the #Name annotation.
AbstractProcessor has another utility member - processingEnv. Use its getFiler method to createSourceFile.
Then you have to modify your compilation a little bit. You must compile your processor separately and before the other classes. After the processor is compiled and you are compiling the other classes you must tell the compiler about your processor. If you are using the command line you would add -processorpath /path/to/processor/class[es] and -processor qualified.name.of.processor.
The advantages of this approach over the class path scanner is that everything happens at compile time. So for example, if you accidentally add a #Name annotation to a Bar element, then you can have the processor throw a compile time error (if you wish the processor can ignore it). Then you can fix it before the product ships. With a class path scanner, any error thrown is a run time error - which the user will see.
The disadvantage of this approach is also that everything happens at compile time. This makes it harder to dynamically add classes to the project.
What you need is a Classpath scanner. I have used Metapossum Scanner (it won out because it is in the mvn repo) to scan for annotated Classes, but I do not think it would scan for annotated Fields.
The other option I looked into was Reflections. I have not used Reflections, only researched it. The documentation has a getFieldsAnnotatedWith query that seems like what you need.
Be forewarned, the Classpath scanners are slooow and get slooower the more you have in your Classpath.
No not really (not from code).
A solution would be to put them all in a class, and then iterate on the Fields (getFields()) of the class and check for Annotations (getAnnotation())
You may want to have a look at Scannotation! It may solve your problem!!!
Scannotation is a Java library that creates an annotation database from a set of .class files. This database is really just a set of maps that index what annotations are used and what classes are using them.
PS.: VRaptor framework uses it internally!

Categories

Resources