Trying to find a way to wraps an object, which is auto generated based on some model with lots of getters and setters. For example:
class ObjectToWrap {
public int getIntA();
public int getIntB();
... // Tons of other getters
}
I have to create a wrapper that wraps this object and use some annotation that generates methods from ObjectToWrap for me. Code looks like the following:
class Wrapper {
private ObjectToWrap obj;
public int getIntA() {
return obj.getIntA();
}
public int getIntB() {
return obj.getIntB();
}
... // Tons of other getters
}
Is there an annotation to do this? I just don't want to make the code look lengthy.
Take a look at Project Lombok which has a #Delegate annotation which does exactly what you want.
#Delegate documentation
I think you would be able to do this:
import lombok.Delegate;
class Wrapper {
//the types field in the annotation says
//to only auto generate deleagate methods
//for only the public methods in the ObjectToWrap class
//and not any parent classes if ObjectToWrap extended something
#Delegate(types = ObjectToWrap.class)
private ObjectToWrap obj;
}
If you are using the maven build infrastructure with dependency management, you could have a dependent sub-project that collects the generated sources as-is (not as code). Another sub-project could then generate real sources out of them (source code transformation) as zip, which then could be imported by maven in the main project as pre-compile target.
On that basis you could use dynamic proxy classes, or even immediate generated classes.
The only other alternative would be to use the java scripting API, and do the business in JavaScript or so. Loosing the type safeness of java and lowering the software quality.
Unfortunately the alternative of hybrid usage of another JVM language I cannot consider productive. The very nice and powerful Scala still is too wild/complex/ticklish.
Related
I am working with AspectJ at the moment.
I seperated AspectJ code in a dependency.
Within that dependency everything works as intended.
But as soon as I import it in another project only some functionality does not work anymore.
When using the defaultImpl of #DeclareParents, the interface is shown within the compiled code but not the default Implementation.
Here is my code to show what I mean (every code snippet is its own File):
AspectJ code:
public interface IAspect
{
String hello();
}
public class IAspectDefaultImpl implements IAspect
{
#Override
public String hello()
{
return "hello";
}
}
#Aspect
public class AspectJ
{
#DeclareParents(value = "#SomeAnnotation*", defaultImpl = IAspectDefaultImpl.class)
private IAspect implementedInterface;
}
Target Class in a different project:
#SomeAnnotation
public class MyClass
{
private final int myValue;
public MyClass(final int wert)
{
this.myValue = wert;
}
public int getMyValue()
{
return myValue;
}
}
Maven throws me:
The type MyClass must implement the inherited abstract method IAspect.hello()
Which implies that it works partially.
When looking at the decompiled .class files the targeted Class does in fact implement IAspect.
The method defined in IAspectDefaultImpl is still missing tho.
My pom is set up like in this example.
I am not sure where I should start to look for errors.
Any help is apreciated.
Thanks for the MCVE. But hey, you don't use Git in order to commit 7z or ZIP archives, you ought to commit source code. I forked your project and fixed that, restructured and simplified your POMs and also fixed the main problem.
See my pull request and the commits in it for further details.
Concerning your problem, I can confirm that it occurs if you use #DeclareParents the way you do in an aspect library.
Actually, according to AspectJ maintainer Andy Clement there are certain problems with #DeclareParents when using it to provide parent interfaces + implementations in annotation style. The native AspectJ syntax via declare parents is not affected by that, but for annotation-style syntax Andy provided an alternative called #DeclareMixin, see the AspectJ manual. There he mentions that he is even considering to deprecate the defaultImpl argument of #DeclareParents in favour of #DeclareMixin.
So my bugfix (or workaround) for your problems is to actually replace
#DeclareParents(value = "#de.example.aspect.SomeAnnotation *", defaultImpl = IAspectDefaultImpl.class)
private IAspect implementedInterface;
by
#DeclareMixin("#de.example.aspect.SomeAnnotation *")
public static IAspect createIAspectImplementation() {
return new IAspectDefaultImpl();
}
This works with aspect libraries.
I will discuss with Andy about whether it makes sense to file a bug ticket for your problem or if he won't fix it anyway because there is a viable and recommended alternative.
I need to generate a jar library in three different versions. Some methods of the classes are marked with annotations that specify the version when they were added. For example:
public class A {
#SinceVersion(2)
public int getTotal() {
// do something...
}
#SinceVersion(5)
public int getMax() {
// do something...
}
#SinceVersion(4)
public int getAverage() {
// do something...
}
}
Then:
When I generate my-library-2.jar, only getTotal() should be included.
When I generate my-library-4.jar, only getTotal() and getAverage() should be included.
When I generate my-library-5.jar all three methods should be included.
This is a simplified example. The real problem spans 300 classes, 10 versions, with 6 subversions each.
You may wanna look into implementing an annotation processor.
Annotation processors run during compile time and are used to create and/or manipulate code, and I use them to create boilerplate code, as well as code that's otherwise tedious to maintain manually, but has a clear structure.
Why not use source control (git, etc.) to manage your releases? when you tag a release with v2.0, then code up to that point will have only getTotal(). Then v4.0 comes a long, and the code up to that point will have getTotal() and getAverage(), and so on.
Each release can be managed and built independently, and produce the specific jar file for that specific release.
Your client will then include whichever version of the jar it needs.
I wanted to post the solution I found in case is of help for someone else.
After researching for a week I finally found Apache Commons BCEL. This library allows you to inspect and modify a class loaded in the JVM; once modified it can be written to the file system as a .class file.
A piece of code like this produces a new class file without some methods:
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.AnnotationEntry;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ClassGen;
JavaClass clazz = Repository.lookupClass("test.House");
ClassGen cg = new ClassGen(clazz);
Method m = findMethod(clazz, "close");
AnnotationEntry ae = m.getAnnotationEntries(); // process annotations here...
cg.removeMethod(m); // remove the method you want to remove
JavaClass modifiedClazz = cg.getJavaClass();
modifiedClazz.dump("./gen/test/House.class"); // save to a new file
By walking the whole tree of classes (or a subpackage tree) it's possible to find all affected classes, process their methods, and remove them if the annotation values match some criteria.
I need to support two versions of a dependency, which have the same API but different package names.
How do I handle this without maintaining two versions of my code, with the only change being the import statement?
For local variables, I guess I could use reflection (ugly!), but I use the classes in question as method argument. If I don't want to pass around Object instances, what else can I do to abstract from the package name?
Is it maybe possible to apply a self-made interface - which is compatible to the API - to existing instances and pass them around as instance of this interface?
I am mostly actually using xtend for my code, if that changes the answer.
Since you're using Xtend, here's a solution that makes use of Xtend's #Delegate annotation. There might be better solutions that aren't based on Xtend though and this will only work for simple APIs that only consist of interfaces with exactly the same method signatures.
So assuming you have interfaces with exactly the same method signatures in different packages, e.g. like this:
package vendor.api1
interface Greeter {
def void sayHello(String name)
}
package vendor.api2
interface Greeter {
def void sayHello(String name)
}
Then you can combine both into a single interface and only use only this combined interface in your code.
package example.api
interface Greeter extends vendor.api1.Greeter, vendor.api2.Greeter {
}
This is also possible in Java so far but you would have to write a lot boilerplate for each interface method to make it work. In Xtend you can use #Delegate instead to automatically generate everything without having to care how many methods the interface has or what they look like:
package example.internal
import example.api.Greeter
import org.eclipse.xtend.lib.annotations.Delegate
import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor
#FinalFieldsConstructor
class GreeterImpl implements Greeter {
#Delegate val Api delegate
}
#FinalFieldsConstructor
class Greeter1Wrapper implements Greeter {
#Delegate val vendor.api1.Greeter delegate
}
#FinalFieldsConstructor
class Greeter2Wrapper implements Greeter {
#Delegate val vendor.api2.Greeter delegate
}
Both Greeter1Wrapper and Greeter2Wrapper actually implement the interface of both packages here but since the signature is identical all methods are forwarded to the respective delegate instance. These wrappers are necessary because the delegate of GreeterImpl needs to implement the same interface as GreeterImpl (usually a single delegate would be enough if the packages were the same).
Now you can decide at run-time which version to use.
val vendor.api1.Greeter greeterApi1 = ... // get from vendor API
val vendor.api2.Greeter greeterApi2 = ... // get from vendor API
val apiWrapper = switch version {
case 1: new Greeter1Wrapper(greeterApi1)
case 2: new Greeter2Wrapper(greeterApi2)
}
val example.api.Greeter myGreeter = new GreeterImpl(apiWrapper)
myGreeter.sayHello("world")
This pattern can be repeated for all interfaces. You might be able to avoid even more boilerplate by implementing a custom active annotation processor that generates all of the required classes from a single annotation.
I'm having troubles trying to get my external Java project so I can use Android classes on it as well. The library is already integrated on the Android project. For instance: I have several model classes on it that I would want to implement Parcelable so they can be seriallized accordingly, but none of the Android classes are available on them.
Clarification I only did this in order to try to solve the issue
So far I've only tried:
Changing and matching the external library's package:
Package name in Android
com.domain.androidproject
Library's package originally
com.domain.libproject
Changed to:
com.dommain.androidproject.libproject
But no luck so far. I imported the library as a Gradle external project vía:
compile project(path: ':LibProject')
Thank you for your help.
You'll have to define a binding between your pure java library and android. You could use Dependency injection to inject the models using the class signature, and then define the parcelable models inside the app (or into another project, like a plugin). Or you could achieve the same using generics. keep in mind, since the java library is already compiled, technically, you can't change it by importing it into the android project (I've seen people "rewriting" some files from a dependency and then adding them with the whole original path to fool the classpath, but that's highly risky since you are not gonna be able to interact with the rest of the dependency's code and if something changes, the thing will break).
if you have access to the pure java's library sourcecode, then modify it to use factories or providers of models. If not, extend the models, add parcelable support, and attempt to use those instead of the original model classes.
Example:
let's suppose we have a model and some functions using it:
public class myModel{
private int id;
private String name;
public void setId(int id){
this.id = id;
}
//more getters and setters
}
public interface myModelCreator<T>{
public myModel create(T toModel);
public T uncreate(myModel fromModel);
}
public static void doSomething(myModel model){
//some library operations
}
Now, in the android project:
public class myAndroidModel extends myModel implements Parcelable{
/*Implements the parcelable methods using the class accessors, or you can change the myModel members to protected.*/
}
public class myAndroidModelCreator implements myModelCreator<myAndroidModel>{
#Override
public myModel create(myAndroidModel toModel){
//create the myModel using the parcelable class.
}
#Override
public myAndroidModel uncreate(myModel fromModel){
//reverse operation.
}
}
Now, in the android project, you can use the parcelable subclass everywhere, and everytime you need to call the library, you can supply the creator interface using the parcelables as arguments.
Another alternative would be changing the library method signatures to something like this:
public static void<T extends myModel> doSomething(T model){
//some library operations
}
So you can directly consume the parcelable subclasses. But depending on your hierarchy, that may be not possible. Lastly, you could attempt to implement dependency injection into the java project using Guice and Roboguice in the android project. Since roboguice uses guice, it is possible they can interoperate, but that's a long shot.
I like Fco P.'s answer, but for the sake of completness, here is an alternative answer.
Use json to serialize objects, rather than Parcelable. You can then put your serialized json as a string extra in intent or as string in bundles.
it's faster to implement than using Parcelable, with libraries such as Google GSON or Square moshi.
it's less performant than Parcelable
Generally if you want to make use of classes in another project/library:
File -> New -> Import Module -> Navigate to the directory of an old project/Library -> Ok
Check off the modules you want to import -> OK
Right click the app module -> Open Module Settings -> dependencies -> + -> Module -> The new Module.
Your project should then be usable in whatever project you just did that for.
Create an android library project with packagename com.domain.libproject
Copy all the sources in src folder.
Update jar dependencies in build.gradle and after that you can make your class in the library parcelable.
Let me know if any issues.
Best regds
I am building an Android app. Now, I have a source code for API #1, I should get it adapted for API #2. Then I will publish the both versions for API #1 and API #2 in different packages. I can't use something like values-en, because both versions can be used worldwide. Also, the user may not have choice.
As the new version will use same UI and DB logic, (and because now the code is erroneous,) I don't want to separate the code. If i were coding in c or c++, I must use #ifdef and Makefile. However, I'm in Java. It's possible to run the API-dependent code by determining the package name in runtime, but it's somewhat weird.
I think I can use annotations. What I expect is:
package foo.app;
public class API {
public boolean prepare() { ... }
#TargetPlatform(1)
public void open() { ... }
#TargetPlatform(2)
public void open() { ... }
}
and use only one of them. Also, this is good:
package foo.app;
public class R {
#TargetPlatform(1) com.example.foo.app.R R;
#TargetPlatform(2) net.example.foo.app.R R;
}
Just defining an annotation is simple. What I don't know is, how can I exclude unused duplicates from build or execution, or so on? If the work can be done in this way, I can do anything.
You cannot use annotations for that.
It would be better to hide the implementation specific classes behind an interface.
public interface Api {
boolean prepare();
void open();
}
To create a Api instance use a factory class:
public class ApiFactory {
public static Api createApi() {
if(isTargetPlatform1())
return new com.example.foo.app.Api();
else
return new net.example.foo.app.Api();
}
private boolean isTargetPlatform1() {
// determine the current platform, e.g. by reading a configuration file
}
}
In all other places you only refer to the Api interface and ApiFactory class.
Use it like that:
Api api = ApiFactory.createApi();
api.open();
// ...
A more advanced solution would be to use dependency injection.