Get Android R class when passing value with annotation? - java

I have this annotation BindPrefRes.class
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.SOURCE)
public #interface BindPrefRes {
#StringRes int value();
}
Now when I use that annotation in any Android project like this:
#BindPrefRes(R.string.app_name) String appName;
And I'm trying to get the value with javax.annotation.processing.AbstractProcessor
int value = element.getAnnotation(BindPrefRes.class).value()
I get the actual int (2131230753), which I understand is the expected behavior. But how do I parse this int back to field (R.string.app_name), or at least string representation of that field ("R.string.app_name")?
The source code is on GitHub if I'm being unclear.

Short answer: you should avoid doing so whenever possible, because the required information might not be available at all. For example, if Gradle (or other build systems) ever start supporting incremental builds with annotation processors, the R class might not be present on compilation path by the time your processor runs.
Long answer: there are two ways:
Ugly
Scan the source code for R classes
Iterate over every field in found R classes and add all constants to Map
Associate the value, received from annotation with constant from the Map
One implementation of this approach (which for some reason relies on non-public Javac internals) can be found in ButterKnife sources).
Another one is maintained by Android Annotations projects (you can copy it or access it directly by making your processor a plugin for Android Annotations processor).
Also ugly
Use Oracle proprietary semi-stable Trees API to directly introspect source code of #BindPrefRes(R.string.app_name). I recommend going that way whenever possible, because it has fewer side-effects.
Obtain a TreePath for annotation value, cast it to ExpressionTree and manually parse contents.

Related

Compile-time annotation processing

Is there way to do compile-time annotation processing in Java?
Consider this example:
#Name("appName")
private Field<String> appName;
public void setAppName(String name) {
appName.setValue(name);
}
public String getAppName(String name) {
return appName.getValue();
}
public void someFunction() {
String whatFieldName = appName.getName();
}
Where the annotation Name will be processed at compile-time to set the value for Field That is without the common runtime annotation processing. As such, when appName.getName(); (the Field) is accessed it will return the typed value.
Yes, there is, but, no, it cannot change existing files. You can 'plug in' to the compiler and be informed of any annotations; as part of this, you can see signatures (so, field declarations, method signatures, types, etc) but no contents (so not the expression used to initialize a field, and not the contents in the {} of a method declaration), and you can make NEW files, even java files, but you can't edit existing ones.
Project Lombok does edit them, but that is quite the framework to make that possible.
There are some crazy tricks you can use. Project lombok uses one trick (reflect its way into compiler internals, fix everything from there, install agents and plugins in IDEs). Another trick is to use a java source file as a template, of sorts. You name your class some funky (so if you want, say, public class AppDescriptor, you'd actually make the java file AppDescriptorTemplate.java and put public class AppDescriptorTemplate inside. This file has the annotation precisely as you pasted. Your annotation processor can then, during compilation, generate AppDescriptor.java, writing the impls of all methods as simple pass-throughs (a field of type AppDescriptorTemplate is generated, and all methods in ADT are copied over, and the implementations are all one-liners that just invoke that method on the template class). The template class can be package private. In this specific scenario it sounds like you can generate virtually the whole thing based off of pretty much only "appName", though.
Lombok plugs straight into the build and is therefore virtually entirely transparent, in the sense that you simply type in your IDE and the methods it generates just appear as you type, whereas 'normal' annotation processors that e.g. use the XTemplate trick do not work that way and require the build system to kick in, every time. It can be a bit of a productivity drain.

What is the purpose of annotations in Java?

I understand that annotations serve a purpose to modify code without actually BEING code, such as:
#Author(
name = "Benjamin Franklin",
date = "3/27/2003"
)
But I don't understand how using the annotation is any better/ clearer/ more concise than just saying name = "Benjamin Franklin" ? How does the addition of annotations strengthen the code?
EDIT: Sorry for another questoin, but I know that #Override can help prevent/ track spelling mistakes when calling methods or classes, but how does it do that? Does it help the actual program at all?
Annotations are just metadata. On their own they serve little to no purpose. There must be an annotation processor, either at the compiler or run time level that uses them for something.
With an annotation like
#Author(
name = "Benjamin Franklin",
date = "3/27/2003"
)
for example, some annotation processor might read it with reflection at run time and create some log file that this author wrote whatever it's annotating on that date.
Annotations are metadata.
#Override annotation is used to make sure that you are overriding method of a superclass and not just making a method with the same name. Common mistakes here consist of:
spelling the method's name wrong
equal(Object o) instead of equals(Object o)
putting different set of arguments
MyString extends String { public boolean equals(MyString str) {} }
equals(MyString str) is not overriding the method equals(Object o) and therefore will not be used by standard Java comparators (which is used in some standard functions, such as List.contains() and this is prone to error situation).
This annotation helps compiler to ensure that you code everything correctly and in this way it helps program.
#Deprecated annotation doesn't make program not to compile but it makes developers think about using the code that can and/or will be removed in a future releases. So they (developers) would think about moving onto another (updated) set of functions. And if you compile your program with the flag -Xlint compilation process will return with an error unless you remove all usages of deprecated code or explicitly mark them with annotation #SuppressWarnings("deprecation").
#SuppressWarnings is used to suppress warnings (yes, I know it's Captain Obvious style :)). There is a deprecation suppression with #SuppressWarnings("deprecation"), unsafe type casting with #SuppressWarnings("unchecked") and some others. This is helpfull when your project compiler have a compilation flag -Xlint and you cannot (or don't want to) change that.
There are also annotation processors that you integrate into your program build process to ensure that program code meets some sort of criteria. For example with IntelliJ Idea IDE annotation processor you can use #Nullable and #NotNull annotations. They show other programmers when they use your code so that can transfer null as a certain parameter to a method or not. If they transfer null it will cause exception during compilation or before executing a single line method's code.
So annotations are quite helpful if you use them to their full potential.
Annotations are most likely used by other programs. Examples include:
#Override
IDE (compiler?) ensures that the signatures match
#Deprecated
IDE marks occurences, compiler warning
#FXML
JavaFX can use these annotations initialize variables in a controller class when an .fxml File is inflated (see http://docs.oracle.com/javafx/2/get_started/fxml_tutorial.htm). They are also used by JavaFX Scene Builder.
Annotations works as a way to marking up the code. Several frameworks uses it, and some others make a great use of it producing your own.
Besides, is important to understand that annotations are the equivalent to meta-data, but is much more than that, since it works as a tag language for the code.
Java #Annotation
#Annotation(from Java 5) adds a metadata which are used for instruction in compile, deployment and run time. It is defined by RetentionPolicy
RetentionPolicy defines a lifetime
RetentionPolicy.SOURCE: It is visible only in compile time(#Override, #SuppressWarnings, #StringDef). For example it can be used by apt to generate some code
RetentionPolicy.CLASS: It is visible in compile and deployment time(.class). For example it can be used by ASM or Java AOP paradigm like AspectJ
RetentionPolicy.RUNTIME: It is visible in deployment and run time. For example it can be used java reflection using getAnnotations(). Dagger 2 uses #Scope annotation
Create a custom Annotation
#Retention(<retention_policy>) //optional
#Target(<element_type>) //optional to specify Java element like, field, method...
#Inherited // optional will be visible by subclass
#Documented // optional will be visible by JavaDoc
#interface MyAnnotation {
//attributes:
String someName();
}
using
#MyAnnotation(someName = "Alex")
public class SomeClass {
}

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!

How to automatically remove methods in java code

I need to remove some methods in a large java project and I was wondering if there are tools that would help me do so. I would basically specify a signature and a source folder in which all the matching method would be removed.
It no such thing exists then I will write a script.
Today, there are a couple of way to achieve this. For example, you can tell the Eclipse compiler to give you the AST (see my blog for an example). You can navigate the AST to find methods and remove nodes to change the source.
This makes it easy to keep the code valid. Using the Eclipse code formatter, you can clean up the formatting afterwards.
Projects like MoDisCo go one step further by analyzing a whole project and giving you ways to search the whole thing. I just found that the documentation for MoDisCo is not very helpful when you're a beginner.
If you use eclipse or other powerful IDE you may have built in support for doing this.
See I can't delete a method using eclipse refactoring? for a way to achieve what you want.
You're talking about a "source transformation", and Google turns up the existence of BeautyJ for Java:
http://beautyj.berlios.de/
"BeautyJ can convert Java source code to XML and back. BeautyJ introduces the XJava format which stores classes or interfaces and their members together with associated source code and Javadoc comments in a single XML file. BeautyJ opens up a wide range of possibilities for automatic structuring of Java source code either by its Sourclet API or by converting Java source code to XML and allowing any external transformations (for example by applying XSLT)."
No clue whether your case is so simple that a script would be better than going through the trouble of learning how to use such a program, though. Although it may be worthwhile to get it in your toolchain for other purposes as well.
Our DMS Software Reengineering Toolkit with its Java Front End could be used for this.
DMS parses languages (using its front ends, in this case Java), builds ASTs and symbol tables, and provide facilities to manipulate the ASTs based on custom analysis. In this case OP wants to provide a method signature (presumably with a context in which it should be interpreted because otherwise the types used in the signature might not be defined), look that signature up in the symbol table, find the point of declaration (this is in the symbol table) as a AST node, and then apply a rewriting rule that replaces the declaration with an empty declaration.
An analysis he may wish to perform on the found method is whether is it used or not. We have that information in the symbol table, too.
You can use something in Java to batch-modify your Java project. The trick is to use a custom annotation processor. With annotation processor you can modify almost everything.
For an example, this library ( for ecilipse and IDEA ) modifies some String field members to it's /**multiline*/ docs.
Studying the library gives me the power to modify the method body as well.
Today, I want to strip out some unwanted methods in the android support library( used as editable local module ). I can manually remove them, but it would be hard to keep udpate after that.
So, instead of using script or mere-hand to modify the code directly, I decide to write another annotation processor which allow you to remove some class members.
For IDEA, the concept-proving code is very simple:
// the custom annotation
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.SOURCE)
public #interface StripMethods {
boolean strip() default true;
String key() default "";
}
// processing the custom annotation
#Override
public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
Set<? extends Element> fields = roundEnv.getElementsAnnotatedWith(StripMethods.class);
for (Element field : fields) {
StripMethods annotation = field.getAnnotation(StripMethods.class);
ElementKind KIND = field.getKind();
if(KIND == ElementKind.CLASS) {
// the class declaration
JCTree.JCClassDecl laDcl = (JCTree.JCClassDecl) elementUtils.getTree(field);
// the definition tree list
ArrayList<JCTree> defs = new ArrayList<>(laDcl.defs);
// remove the second member which in this case is a string field
defs.remove(2);
// finally modify the class definition
laDcl.defs = List.from(defs);
}
}
}
#StripMethods
public class Test {
// the first member is the default constructor
static {
}
static final int FieldToRemove = 0;
#Test
public void test() {
int variableToRemove = FieldToRemove;
}
}
the result error caused by the member removal:
Test.java:10: error: cannot find symbol
int variableToRemove = FieldToRemove;
^
symbol: variable FieldToRemove
location: class Test
Still a long way to go. I will publish the code when it's finished.
Done. see https://github.com/KnIfER/Metaline
Exmaple usage, removing NightMode from androidx/appcompat:
#StripMethods(key="Night")
public class AppCompatActivity
...
#StripMethods(key="Night")
public abstract class AppCompatDelegate
...
#StripMethods(key="Night")
class AppCompatDelegateImpl
...
I looked for quick solutions and did not find any, I used different tools to get there:
grep to find all the files containing the main methods I wanted
to remove
find/replace in these files to make those methods private
Eclipse clean-up with just one rule: remove unused
private methods
It did the job with the minor side effect of removing other unused private methods. It is not a big deal in the context of that project since pretty much all the files were previously saved with a save action removing unused private methods.
Thanks all for the input, it may be applicable later on.

Java Builder generator problem

In a project of mine I have two packages full of DTOs, POJOs with just getters and setters. While it's important that they are simple java beans (e.g. because Apache CXF uses them to create Web Service XSDs etc.), it's also awful and error-prone to program like that.
Foo foo = new Foo();
foo.setBar("baz");
foo.setPhleem(123);
return foo;
I prefer fluent interfaces and builder objects, so I use maven / gmaven to automatically create builders for the DTOs. So for the above code, a FooBuilder is automatically generated, which I can use like this:
Foo foo = new FooBuilder()
.bar("baz")
.phleem(123)
.build();
I also automatically generates Unit tests for the generated Builders. A unit test would generate both of the above codes (builder version and non builder version) and assert that both versions are equivalent in terms of equals() and hashcode(). The way I can achieve that is to have a globally accessible Map with defaults for every property type. Something like this:
public final class Defaults{
private Defaults(){}
private static final Map<Class<?>, Object> DEFAULT_VALUES =
new HashMap<Class<?>, Object>();
static{
DEFAULT_VALUES.put(String.class, "baz");
// argh, autoboxing is necessary :-)
DEFAULT_VALUES.put(int.class, 123);
// etc. etc.
}
public static getPropertyValue(Class<?> type){
return DEFAULT_VALUES.get(type);
}
}
Another non-trivial aspect is that the pojos sometimes have collection members. e.g.:
foo.setBings(List<Bing> bings)
but in my builder I would like this to generate two methods from this case: a set method and an add method:
fooBuilder.bings(List<Bing> bings); // set method
fooBuilder.addBing(Bing bing); // add method
I have solved this by adding a custom annotation to the property fields in Foo
#ComponentType(Bing.class)
private List<Bing> bings;
The builder builder (sic) reads the annotation and uses the value as the generic type of the methods to generate.
We are now getting closer to the question (sorry, brevity is not one of my merits :-)).
I have realized that this builder approach could be used in more than one project, so I am thinking of turning it into a maven plugin. I am perfectly clear about how to generate a maven plugin, so that's not part of the question (nor is how to generate valid Java source code). My problem is: how can I deal with the two above problems without introducing any common dependencies (between Project and Plugin):
<Question>
I need a Defaults class (or a similar mechanism) for getting default values for generated unit tests (this is a key part of the concept, I would not trust automatically generated builders if they weren't fully tested). Please help me come up with a good and generic way to solve this problem, given that each project will have it's own domain objects.
I need a common way of communicating generic types to the builder generator. The current annotation based version I am using is not satisfactory, as both project and plugin need to be aware of the same annotation.
</Question>
Any Ideas?
BTW: I know that the real key point of using builders is making objects immutable. I can't make mine immutable, because standard java beans are necessary, but I use AspectJ to enforce that neither set-methods nor constructors are called anywhere in my code base except in the builders, so for practical purposes, the resulting objects are immutable.
Also: Yes, I am aware of existing Builder-generator IDE plugins. That doesn't fit my purpose, I want an automated solution, that's always up to date whenever the underlying code has changed.
Matt B requested some info about how I generate my builders. Here's what I do:
I read a class per reflection, use Introspector.getBeanInfo(clazz).getPropertyDescriptors() to get an array of property descriptors. All my builders have a base class AbstractBuilder<T> where T would be Foo in the above case. Here's the code of the Abstract Builder class. For every property in the PropertyDescriptor array, a method is generated with the name of the property. This would be the implementation of FooBuilder.bar(String):
public FooBuilder bar(String bar){
setProperty("bar", bar);
return this;
}
the build() method in AbstractBuilder instantiates the object and assigns all properties in it's property map.
A POJO is an object which doesn't follow the Java Bean spoec. ie. it doesn't have setters/getters.
JavaBeans are not required to have setters, if you don't want them to be called, don't generate them. (Your builder can call a package local or private constructor to create your immutable objects)
Have you looked at Diezel ?
It's a Builder generator.
It handles generic types, so it might be helpful here for the question 2
It generates all the interfaces, and implementation boiler plate based on a description XML file. You might be able, through introspection to generate this XML (or even goes directly into lower API )
It is bundled as a maven plugin.

Categories

Resources