I'm using JavaPoet to generate classes given a list of fields.
Generating a constructor that takes this list of fields and turns it into a list of parameters is fairly straightforward with something like the following:
val constructor = MethodSpec.constructorBuilder()
for ((fieldName, typeName) <- fields) {
constructor.addParameter(typeName, fieldName)
}
I would also like to generate a static method that instantiates a new object of the generated class, which requires me to generate a code block that uses the aforementioned list of fields.
Currently, I'm achieving this with something like the following:
method
.addStatement(s"return new $$T(${fields.map(_._1).mkString(", ")})", className)
It works, but I was wondering if there was a more "idiomatic" way to use JavaPoet to build a list of arguments with the built-in templating support.
In order to avoid mixing Scala's interpolation and JavaPoet's templating it looks like the best way to achieve this is by making a CodeBlock that contains the parameter list and embedding it in the code with a template, as in the following example:
val params = fields.map { case (name, _) => CodeBlock.of(name) }
val paramList = CodeBlock.join(params.asJava)
method.addStatement("return new $T($L)", className, paramList)
The template here makes use of the $L placeholder that will be replaced by a literal (in this case, the assembled list of parameters).
Related
This is the method:
protected <T> TestPageResult<T> getTestPageResutForRequest(MockHttpServletRequestBuilder request) throws Exception {
String responseJson = mockMvc.perform(request).andReturn().getResponse()
.getContentAsString();
TestPageResult<T> response = getObjectMapper().readValue(responseJson,
new TypeReference<TestPageResult<T>>() {
});
return response;
}
I call it like this:
TestPageResult<SomeDto> pageResult = this.<SomeDto>getTestPageResutForRequest(getRequest());
TestPageResult is:
protected static class TestPageResult<T> {
private List<T> items;
private long totalCount = -1;
public TestPageResult() {
}
//omitted getters and setters
}
The resulting pageResult.getItems() contains a List of LinkedHashMap instead of a list of SomeDto. If I were to just hardcode the SomeDto type in the objectMapper.readValue method I'd get the correct results.
What's the problem?
edit: The suggested duplicated did solve my problem - kind of.
I used:
JavaType type = getObjectMapper().getTypeFactory().constructParametricType(TestPageResult.class, clazz);
TestPageResult<T> response = getObjectMapper().readValue(responseJson, type);
Problem is there is no going around not passing down a Class argument to the method. So the method looks ugly due to both passing a generic type and the same thing as a Class. Obviously you can just not pass the generic now but this way a casting would be required and adding SuppressWarnings and so on.
The problem is erasure. All these <T> parameters don't exist in the compiled code, after they're erased. This means that source new TypeReference<TestPageResult<T>>() looks like new TypeReference<TestPageResult>() once compiled, which is not what you want. (Similar to how a List<String> ends up being a List in compiled code, and it's just compile-time validation that you don't add Integers to your String List.)
I think there's roughly two ways to deal with this (in this case), both of these you already stumbled upon:
Either you create a type that properly represents what you want, such as: new TypeReference<TestPageResult<SomeDto>>(), or class SomeDtoPageResult extends TestPageResult<SomeDto> which you can then use in places like readValue(..., SomeDtoPageResult.class);
Or you create a complete class representation, like you were doing with JavaType
What you really want won't work. Your best bet is to tinker and come up with the cleanest code that solves it. Generics let you express really elaborate structures, and when you serialize an actual instance (nested objects), that comes out just fine, but when the classes need to be introspected at runtime, e.g. for deserialization (your use case) or to build a model (e.g. to generate Swagger docs), this becomes problematic.
C# 6.0 introduced the nameof() operator, that returns a string representing the name of any class / function / method / local-variable / property identifier put inside it.
If I have a class like this:
class MyClass
{
public SomeOtherClass MyProperty { get; set; }
public void MyMethod()
{
var aLocalVariable = 12;
}
}
I can use the operator like this:
// with class name:
var s = nameof(MyClass); // s == "MyClass"
// with properties:
var s = nameof(MyClass.OneProperty); // s == "OneProperty"
// with methods:
var s = nameof(MyClass.MyMethod); // s == "MyMethod"
// with local variables:
var s = nameof(aLocalVariable); // s == "aLocalVariable".
This is useful since the correct string is checked at compile time. If I misspell the name of some property/method/variable, the compiler returns an error. Also, if I refactor, all the strings are automatically updated. See for example this documentation for real use cases.
Is there any equivalent of that operator in Java? Otherwise, how can I achieve the same result (or similar)?
It can be done using runtime byte code instrumentation, for instance using Byte Buddy library.
See this library: https://github.com/strangeway-org/nameof
The approach is described here: http://in.relation.to/2016/04/14/emulating-property-literals-with-java-8-method-references/
Usage example:
public class NameOfTest {
#Test
public void direct() {
assertEquals("name", $$(Person.class, Person::getName));
}
#Test
public void properties() {
assertEquals("summary", Person.$(Person::getSummary));
}
}
Sadly, there is nothing like this. I had been looking for this functionality a while back and the answer seemed to be that generally speaking, this stuff does not exist.
See Get name of a field
You could, of course, annotate your field with a "Named" annotation to essentially accomplish this goal for your own classes. There's a large variety of frameworks that depend upon similar concepts, actually. Even so, this isn't automatic.
You can't.
You can get a Method or Field using reflection, but you'd have to hardcode the method name as a String, which eliminates the whole purpose.
The concept of properties is not built into java like it is in C#. Getters and setters are just regular methods. You cannot even reference a method as easily as you do in your question. You could try around with reflection to get a handle to a getter method and then cut off the get to get the name of the "property" it resembles, but that's ugly and not the same.
As for local variables, it's not possible at all.
You can't.
If you compile with debug symbols then the .class file will contain a table of variable names (which is how debuggers map variables back to your source code), but there's no guarantee this will be there and it's not exposed in the runtime.
I was also annoyed that there is nothing comparable in Java, so I implemented it myself: https://github.com/mobiuscode-de/nameof
You can simply use it like this:
Name.of(MyClass.class, MyClass::getProperty)
which would just return the String
"property"
It's also on , so you can add it to your project like this:
<dependency>
<groupId>de.mobiuscode.nameof</groupId>
<artifactId>nameof</artifactId>
<version>1.0</version>
</dependency>
or for Gradle:
implementation 'de.mobiuscode.nameof:nameof:1.0'
I realize that it is quite similar to the library from strangeway, but I thought it might be better not to introduce the strange $/$$ notation and enhanced byte code engineering. My library just uses a proxy class on which the getter is called on to determine the name of the passed method. This allows to simply extract the property name.
I also created a blog post about the library with more details.
Lombok has an experimental feature #FieldNameConstants
After adding annotation you get inner type Fields with field names.
#FieldNameConstants
class MyClass {
String myProperty;
}
...
String s = MyClass.Fields.myProperty; // s == "myProperty"
I have some REST services (consuming and producing application/json) and I use #TypeHint to generate documentation.
Now I have something like this:
import javax.ws.rs.core.Response;
...
#Path("/path")
public class MyClass {
#GET
#TypeHint(MyResponse.class)
public Response getIt() {
MyResponse resp = ... ;
return MyBuilder.build(resp);
}
}
but MyResponse is a wrapper over List<MyType>.
My build method from MyResponse looks like this:
public static Response build(Serializable payload) {
return Response.ok(msr).header(...).build();
}
I want to use directly List<MyType> instead of MyResponse. Which is the best way to use TypeHint in the following code?
#GET
#TypeHint(/* TODO */)
public Response getIt() {
List<MyType> myList = ... ;
return MyBuilder.build(myList);
}
I was thinking to the following options:
#TypeHint(List.class)
#TypeHint(MyType.class)
#TypeHint(List<MyType>.class) -> unfortunately this doesn't work because of Java type erasure.
Question:
Is there a valid alternative for number 3?
Even if the type is a List, number 1 is not useful because my own type has to be annotated with #XmlRootElement and that List is unchangeable (it is from JDK).
There is a workaround for number 2, but it's not quite perfect:
Use number 2 (just to have an available example in the generated HTML documentation - a description for an element that is contained in that list)
Specify that it is a List in Javadoc (E.g.: after the #return word) (it can be emphasized using bold, colors, italics, etc. via HTML tags)
E.g.:
/**
* ...
* #return <strong><font color="blue">List<MyType></font></strong>
*/
Details:
enunciate.version = 1.30.1
Java 7
I have opted for using MyType[].class when using TypeHint instead of List.class. This way the documentation will state "array of MyType" which for my rest-api with json is true.
#TypeHint(value = MyType[].class)
As you know TypeHint is used to give Enunciate a hint about what a JAX-RS resource method returns or accepts as an input parameter.
In your case the return type is being described.
I assume that the ClassReturnedByMyBuildersBuildMethod is a subclass of the javax.ws.rs.core.Response abstract class.
For the code as you showed it what you need to use is the class returned by MyBuilder's build method - #TypeHint(ClassReturnedByMyBuildersBuildMethod.class).
Options 2 #TypeHint(MyType.class) makes no sense. It is not a return type nor an input parameter.
Update 1: with your workaround it can make some sense :)
If you add an input parameter to the getIt method - something like public Response getIt(List<MyType> myList){... you would use option 1(#TypeHint(List.class)) because as you know the org.codehaus.enunciate.jaxrs.TypeHint annotation type element declaration has a Class return type (Class<?> value();) and you cannot use a parameterized type beacuse of the erasure of generic types (the paramterized type share the same class at runtime in this case - List).
But changing the input parameter to getIt(List<MyType> myList) is probably not viable because the list would have to be obtained from the URI ( with javax.ws.rs's #QueryParam or #Context UriInfo ). This question addresses best practices when using list of parameters as input if it may concern you.
Update 2: Option 1 becomes is less viable because of your XmlRootElement constraint.
Update 3: I see no valid alternative for option 3 using the TypeHint annotation as it is.
You will have to go with your custom option 2 workaround.
I wish to instantiate a java class that I have defined in my domain and I want to use it from my FTL code in this way, but I'm getting an error.
<#assign myClassInstance = "com.domain.MyClass"?new())>
Is it possible? What I should change to do it?
MyClass doesn't implements the TemplateModel
Thanks!
There's no built-in function for instantiating arbitrary non-TemplateModel classes... maybe there should be a setting to allow that for ?new. Anyway, for now you can write a TemplateMethodModelEx that does that, and then you can pull that into some of your commonly included/imported templates like <#assign unrestrictedNew = "com.example.UnrestrictedNewMethodModel"?new()> (or just put the instance into the data-model or into the Configuration as a shared variable) and then you can do <#assign myClassInstance = unrestrictedNew("com.domain.MyClass")(arg1, arg2, argN)> in your templates. There are two tricky parts in implementing such a TemplateMethodModel. One is resolving the class name to a Class, for which I recommend env.getNewBuiltinClassResolver().resolve(className, env, null), where env is the current freemarker.core.Environment object. The other is calling the constructor, as then you have to convert parameter values and possibly chose an overloaded constructor. For that I recommend calling ow = env.getObjectWrapper(), see if ow instanceof BeansWrapper (throw exception if it isn't), then do return ((BeansWrapper) ow).newInstance(cl, arguments).
I'm pretty new to Java, so I'm hoping one of you guys knows how to do this. I'm having the user specify both the type and value of arguments, in any XML-like way, to be passed to methods that are external to my application.
Example: javac myAppsName externalJavaClass methodofExternalClass [parameters]
Of course, to find the proper method, we have to have the proper parameter types as the method may be overloaded and that's the only way to tell the difference between the different versions. Parameters are currently formatted in this manner:
(type)value(/type), e.g. (int)71(/int) (string)This is my string that I'm passing as a parameter!(/string)
I parse them, getting the constructor for whatever type is indicated, then execute that constructor by running its method, newInstance(<String value>), loading the new instance into an Object. This works fine and dandy, but as we all know, some methods take arrays, or even multi-dimensional arrays.
I could handle the argument formatting like so: (array)(array)(int)0(/int)(int)1(/int)(/array)(array)(int)2(/int)(int)3(/int)(/array)(/array)... or perhaps even better... {{(int)0(/int)(int)1(/int)}{(int)2(/int)(int)3(/int)}}.
The question is, how can this be implemented? Do I have to start wrapping everything in an Object[] array so I can handle primitives, etc. as argObj[0], but load an array as I normally would? (Unfortunately, I would have to make it an Object[][] array if I wanted to support two-dimensional arrays. This implementation wouldn't be very pretty.)
What you're really looking for is JSON, and one of the Java kits for handling it.
Yes there is, and it's called java.lang.Object. You can even assign arrays like int[][] to an variable declared as java.lang.Object.
But I fear that's not what you wanted. It seems that you are writing a client-service framework -- the client (your user) pass some data to the service (your app). There're existing libraries that can do the same thing, e.g., Thrift, Protobuf. If you are looking for XML-based solution, there is SOAP.
I think what you are looking for here is a way to dynamically call Java methods based on attributes described inside an XML file.
If that's the case, you can explore the Java Reflection API.
Example class:
package foo;
public class Bar {
public void doSomething(String x) {
System.out.println("This is a method doSomething that takes in a String parameter");
}
public void doSomething(String [] arr, String str) {
System.out.println("This is a method doSomething that takes in a String arr parameter and a String parameter");
}
}
To dynamically use the methods in this class, do the following:
Class c = Class.forName("foo.Bar");
Object newInstance = c.newInstance();
Method method = c.getMethod("doSomething", String.class);
// This will locate the first method
method.invoke(newInstance, "Hello World");
Method method = c.getMethod("doSomething", String[].class, String.class);
// This will locate the second method
String [] arr = {"Hello", "World"};
method.invoke(newInstance, arr, "Hello World");
So you can specify in your XML file as follows:
<method>
<name>doSomething</name>
<params>
<param>java.lang.String</param>
</params>
</method>
<method>
<name>doSomething</name>
<params>
<param>java.lang.String[]</param> // or any other annotation to indicate that its an arr
<param>java.lang.String</param>
</params>
</method>
Then, read the XML file and use it to find the Java methods dynamically.
To dynamically create an array:
Class c = Class.forName("java.lang.String");
int length = 5;
Object o = Array.newInstance(c, length); // o is a String arr of length 5
String [] arr = (String []) o;