How do I write a Java inner class with custom properties at compile time using annotations?
For instance, I want this :
#Generate
class Person {
String firstname, lastname;
}
to generate:
class Person {
String firstname, lastname;
public static class $Fields {
public static String firstname = "firstname";
public static String lastname = "lastname";
}
}
How can I write the interface:
#Retention(RetentionPolicy.SOURCE)
public #interface Generate {
// ...
}
I understand I need to do some kind of AST transformation to make this magical.
I am also aware of project lombok, but I want to know what the least common denominator is with a simple example, preferably within one method, and preferably something that a good editor would consider automatically, for instance RetentionPolicy.SOURCE for the javac compiler, which can be used in Intellij IDEA.
Project lombok is a beast code wise and is tough place to start.
It must be simpler than that, is it not?
Any ideas?
You can do this by reflection, but your new class won't be an inner class; but be warned, you will lose static type safety.
It can be done in 2 steps:
Read the annotated class via reflection and transform it into a String which represents the source code of your new class.
Write this string to file, compile this String using the Java compiler API and then load and instantiate the new class, all programatically; see exact steps here.
Alternatives to achieving similar functionality can also be obtained by bytecode instrumentation (see cglib or javassist) or maybe even with proxies.
Related
I am trying to generate a CSV mapping for the fields of a class in Java in an automatic way since I need to use it several times.
I have the following method for trying to get the field names: (where CSV header is something like "DB_NAME|FIELD_NAME|ADDITIONAL_F1|ADDITIONAL_F2")
package util;
import java.lang.reflect.Field;
public class CsvAttributesMappingGenerator {
public static String generateCsvAttributesMapping(Class<?> model) {
StringBuilder csvBuilder = new StringBuilder();
Field[] fieldList = model.getDeclaredFields();
for (Field field : fieldList) {
//field.setAccessible(true);
csvBuilder.append(field.getName().replaceAll("(.)(\\p{Upper})", "$1_$2").toUpperCase());
csvBuilder.append("|");
csvBuilder.append(field.getName());
csvBuilder.append("||\n");
}
return formatOutput(csvBuilder.toString());
}
private static String formatOutput(String classText) {
String delimiter = "\n******************************\n";
return String.format("%s%s%s", delimiter, classText, delimiter);
}
}
and a test call like:
import objects.User;
import org.junit.Test;
import util.CsvAttributesMappingGenerator;
public class CsvAttributesMappingGeneratorTest {
#Test
public void testGenerationWithObject() {
System.out.println(CsvAttributesMappingGenerator.generateCsvAttributesMapping(User.class));
}
}
The object to be parsed has the following structure:
package objects;
public class User {
private String userName;
private String userEmail;
private int userAge;
private String otherDetails;
// getters, setters and all args constuctor here
}
The output should have multiple rows like FIELD_NAME|fieldName|| where the camel cased item should be collected from the given class. I attempted to use Java Reflection API as I have seen on several examples but I get a strange String output instead. (not the serialized #randomCharsLikeName). Tried toString() and other dirty tricks but nothing worked.
Can someone tip me up a little with this? Or at least tell me if it is possible to do what I tried?
Thanks in advance!
EDIT: the current code prototype presented in the question works in an isolated environment (separate new project) and displays the expected output. It does not work though integrated within the whole application I am trying to integrate it into. I will keep researching and let you know the root cause (in the real app I am using also lombok for the classes (
#AllArgsConstructor, #NoArgsConstructor, #Data, #ToString
), but I do not honestly think that this might be an issue while using Reflection)
I found the issue in the meanwhile. While I was play-prototyping the generator, I used:
csvBuilder.append(field.getClass().getName().replaceAll("(.)(\\p{Upper})", "$1_$2").toUpperCase());
which produced outputs like JAVA.LANG.REFLECT._FIELD|java.lang.reflect.Field||
Since I simply forgot that I actually improved it to use actual class object as a parameter to the function. Before asking the question I made some Sonar fixes to the code and did not notice that I fixed a WARN to remove the .getClass() method invocation since I already pass in a class (I thought it would not make a difference since it is just a warning). Moral tip of the day - NEVER EVER ignore warnings.
So the code snippets presented in the question work fine now in an isolated dummy project and also integrated within a more complex project, using the fixed line:
csvBuilder.append(field.getName().replaceAll("(.)(\\p{Upper})", "$1_$2").toUpperCase());
Also as someone suggested in the comments, the field.setAccessible(true); can be removed since it is useless for the purpose of the method.
My goal is to take a XML and/or XSD file and dynamically create a Java object that contains costume annotations and load it into the JVM. A 3rd party library would then look for those objects containing that annotation and perform some function to it. example output of what the java object would look like is as follows
import org.optaplanner.core.api.domain.entity.PlanningEntity;
import org.optaplanner.core.api.domain.variable.PlanningVariable;
#PlanningEntity
public class NameAssignment extends ResourceAssignment{
private String name;
#PlanningVariable(valueRangeProviderRefs = { "PlannerCountRange" })
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The part i'm struggling with is generating the annotation fields #PlanningEntity and #PlanningVariable(valueRangeProviderRefs = { "PlannerCountRange" }) before, during or after the unmarshalling of the XML.
I've been trying to figure it out with JAXB, Dynamic JAXB, Dynamic JAXB, JavaAssist(for byte code manipulation), JCypher, and XJC(Just for compiling the classes). I even thought about dumping Java altogether and using Groovy.
I'm starting to feel like I'm over complicating this and need some guidance. This is a "from scratch" project so I have zero constraints on how I implement it.
Any Help would be greatly appreciated.
With jaxb2-annotate-plugin you may inject any static annotations into generated code just provide binding.
Please look for answer Custom annotation with jaxb2-annotate-plugin and XJC tool for details and examples how to use it.
I have an annotation in my java project which has some default strings in it:
public #interface MyInterface {
String message() default "Dependency for field; must be set here";
// ...
}
How can I do internationalization here? In my classes I would load the string via a ResourceManager
public class ValidationDocument {
private String message = ResourceManager.findLiteral("ValidationDocument", "default.message");
// ...
}
I can't load the ResourceManager in the annotation definition. What would be a good way to do the internationalization here?
You're right, you cannot do it, because annotations are evaluated at compile time and thus you can only use constants, or expressions that only involve constants. Information that can only by available at run-time, such as one retrieved by calling methods, even static ones, therefore cannot be assigned in annotations.
Annotations are not designed to be dynamically modified at run-time, so you will need to change your approach.
I could only suggest to do something like:
public #interface MyInterface {
String messageKey() default "myinterface.mykey";
// ...
}
Then, your code that actually references the #MyInterface annotation instance, would use the messageKey to look-up the message in the ResourceManager. Might work depending on what you're trying to achieve with it.
Suppose I have a class called Person:
public class Person
{
private String name, ID, location;
}
Instead of writing individual accessors for name, ID, and location, is there anyway to do something like this:
public String get%s
{
return this.%s
}
Where "%s" stands as a sort of place holder for the name of the String which I want to "get". This way, for the sake of convenience and efficiency, I will be able to write one method to access all three Strings. Thanks.
Java doesn't have this feature, but if you use Eclipse, you can automatically generate setters and getters for your class members.
I'm not aware of anything like that in Java, but Groovy does almost exactly that, exposes JavaBean get/sets as if they were just public.
class Person {
private String name
private String location
private String id
String toString() {
"Person( name: $name, location: $location, id: $id )"
}
}
def bob = new Person()
bob.name = "bob";
bob.location = "seattle"
def robert = new Person( name: "robert", location: "seattle" )
println "bob is $bob, robert is $robert"
It works with your old school JavaBeans as well. Give groovy a try, it has stuff that Java should have had all along and it interoperates with Java with no impedance mismatch (an ints and int, a BigDecimal is a BigDecimal, I can get all all my Jars, the list goes on and on).
By-in-large, JavaBean get and set method generation has become the function of the IDEs. In Eclipse, you set up your member variables, then select "Source, Generate Getters and Setters..." and select which fields you want to expose an how.
Have a look at Project Lombok, which generates getters and setters at compile time.
Quotes from the feature overview:
#Getter / #Setter:
Never write public int getFoo() {return foo;} again.
#ToString:
No need to start a debugger to see your fields: Just let lombok generate a toString for you!
#Data:
All together now: A shortcut for #ToString, #EqualsAndHashCode, #Getter on all fields, and #Setter on all non-final fields, and #RequiredArgsConstructor!
Your Person class would seems like the perfect target for an #Data annotation.
I use Lombok together with JSF and Hibernate in NetBeans and it works like a charm.
You cannot do such a thing in Java.
You can use Groovy (works perfectly well in a Java project mixed with Java classes), you won't have to worry about declaring getters and setters anymore.
i have a small code generation question.
I have a EJB3 backend that serves DTO objects to a frontend. The frontend uses a configurable binding procedure to map the attributes of the DTO to their forms.
At the moment they are doing it by specifing the attributes as strings in ther configuration. The binding implementation uses reflection to acces the attributes.
Sounds nice but the problem is, each time we change an attribute name in a DTO, this will not lead to a compile error in the frontend because they have just strings.
I'm now looking for a way to create a string constant for each attribute of the class that can be used by the frontend to map the attributes to their forms so that they get compile errors if i made changes in the dto attributes.
Example how it is:
public class CarDTO {
private String vendor;
private String name;
public String getVendor() {}
public String getName() {}
[..]
}
And how it should be:
public class CarDTO {
public static final String VENDOR = "vendor";
public static final String NAME = "name";
private String vendor;
private String name;
public String getVendor() {}
public String getName() {}
[..]
}
I was looking for a maven plugin that is capable of this but without success. Is there any one who nows a tool which can do things like that?
Thanks in advance
martin
Modifying existing class is more difficult then creating new one.
JPA took a interesting approach to solve this problem by creating CarDTO_ class. See http://www.hibernate.org/subprojects/jpamodelgen.html for more details. This approach is much easier. You can look at the hibernate maven plugin that implement the code generation.
If you really want to modify the existing class, then I would recommend using AspectJ with an approach similar to Spring Roo, where the aspect contains the generated code.
Edited (Example using AspectJ)
In this case, we are using AspectJ inter-type declarations that enables you to modify an existing class.
aspect CarAspect
{
public static final String CarDTO.VENDOR = "vendor";
public static final String CarDTO.NAME = "name";
}
Do implement this in maven, you need
a plugin to generate the CarAspect
the aspectj-maven-plugin to compile (weave) the aspect
Also, Eclipse has good support for AspectJ so you can use it there too.
I have a app with a similar frontend approach to access the domain classes, but I have my domain is entirely created via a DSL implemented via Eclipse Xtext, who can be used in a maven build also. There is a sample Java domain DSL project in the xtext distribution, its easy to start from there.
This sure is not a fast "just use a maven plugin" solution but once you get into Xtext it will pay off, especially if you have a lot domain classes, or a lot similar projects.
From my domain DSL I create via code templates and a xtext generator project three classes:
target/generated/mydsl (generated always):
AbstractDomainClass (in this file i have my static string's)
src/main/java (generated once):
ConcreteDomainClass
src/test/java (generated once):
ConcreteDomainClassTest
In the abstract domain class i have all getters and setters and simple persistence stuff in it, in the concrete domain class is the more complex stuff and the test class stands for it self.