Annotation Processor : initialize a field - android

I am writing an annotation processor in android which generates a java file. I am using JavaPoet library for that.
The purpose of generated file:
It should have a list of names of the classes with a particular annotation that my processor supports and provide a public method to get that list.
Now, I've generated the file:
private final List<String> names;
GeneratedFile(ArrayList<String> names) {
this.names = names;
}
public List<String> getNames() {
return names;
}
Now, the problem is: How do I initialize the names field from the processor? The Javapoet api provides an initializer for the field but that only takes a string.
In my processor, I've the list of classes that have my supported annotation. I want to populate this field with that list.

As you already know, JavaPoet offers only a string to specify field initialization. To accomplish your task you have to (I will show you some code that is not specific to your problem, but it can be a good example to show you how to do it):
Get from your annotation processor the list of classes with your annotation (the code is copied from a my library):
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
model = new PrefsModel();
parseBindType(roundEnv);
// Put all #BindSharedPreferences elements in beanElements
for (Element item : roundEnv.getElementsAnnotatedWith(BindSharedPreferences.class)) {
AssertKripton.assertTrueOrInvalidKindForAnnotationException(item.getKind() == ElementKind.CLASS, item, BindSharedPreferences.class);
// store type name somewhere
}
return true;
}
Use this set of TypeName to generate the initial value for you field:
...
Builder sp = FieldSpec.builder(ArrayTypeName.of(String.class), "COLUMNS", Modifier.STATIC, Modifier.PRIVATE,
Modifier.FINAL);
String s = "";
StringBuilder buffer = new StringBuilder();
for (SQLProperty property : entity.getCollection()) {
buffer.append(s + "COLUMN_" +
columnNameToUpperCaseConverter.convert(property.getName()));
s = ", ";
}
classBuilder.addField(sp.addJavadoc("Columns array\n").initializer("{" +
buffer.toString() + "}").build());
...
You will find my library on github. In particular, you have to read the class com.abubusoft.kripton.processor.sqlite.BindTableGenerator.
Hope this information can be still useful.

Related

How to cast List to String[] in Android

In my application I want use ChipView, from this Library : https://github.com/adroitandroid/ChipCloud
In this library for set lists , I should use string[].
In my application I get lists of Tag with this code :
response.body().getData().getTags()
And Tags model is :
#SerializedName("tags")
#Expose
private List<NewsDetailTag> tags = null;
...
public List<NewsDetailTag> getTags() {
return tags;
}
In above library I should add list with this codes :
chipCloud.addChips(someStringArray);
How can I convert List to string[] in android?
Please help me guys.
There is no need for a "conversion" at all! No need to waste memory :)
Take a look at the code of the library and see, what ChipCloud.addChips() does:
public void addChips(String[] labels) {
for (String label : labels) {
addChip(label);
}
}
Its just going through the elements of the array and adding each string individually with the addChip() method.
In your code, you can do this the same way with a list:
List<NewsDetailTag> tags;
String tagString;
ChipCloud chipCloud;
// Get the tags, initialize the chipCloud, etc ...
for (NewsDetailTag tag : tags) {
tagString = tag.getTheStringFromNewsDetailTag();
chipCloud.addChip(tagString);
}
You could even write your own class that extends ChipCloud and add a method that accepts a List parameter.
The only thing thats left to do is to get a String from your NewsDetailTags. But it looks like they are serializable anyways.
try this:
String[] newList = yourList.toArray(new String[]);
hope this works
List<NewsLineTag> tags = response.body().getData().getTags();
List<String> tagStrings = new ArrayList<String>();
//add some stuff
for (NewsLineTag tag : tags) {
tagStrings.add(tag.getSomeTextValueINeed());
}
chipCloud.addChips(tagStrings.toArray(new String[0]));
getSomeTextValueINeed() should be replaced with some method which will provide you with the String you want to show.
Duplicate of Converting 'ArrayList<String> to 'String[]' in Java
Java's List has a pretty convenient toArray() method you can use to convert a List to an Array of the same type.
However, since you have a List<NewsDetailTag> you will have to build the new array yourself.
It will look something like this:
String[] strings = new String[](list.size())
for(int i = 0; i < list.size(); i++) {
array[i] = list.get(i).getStringField();
}
Where getStringField() is whatever property on NewsDetailTag contains the String you want.

In Android, how to choose what items of an ArrayAdapter a ListView can display? [duplicate]

I have a class defined as follows:
public class Person {
private String name;
// constructor and getter/setter omitted
}
I tried to print an instance of my class:
System.out.println(myPerson);
but I got the following output: com.foo.Person#2f92e0f4.
A similar thing happened when I tried to print an array of Person objects:
Person[] people = //...
System.out.println(people);
I got the output: [Lcom.foo.Person;#28a418fc
What does this output mean? How do I change this output so it contains the name of my person? And how do I print collections of my objects?
Note: this is intended as a canonical Q&A about this subject.
Background
All Java objects have a toString() method, which is invoked when you try to print the object.
System.out.println(myObject); // invokes myObject.toString()
This method is defined in the Object class (the superclass of all Java objects). The Object.toString() method returns a fairly ugly looking string, composed of the name of the class, an # symbol and the hashcode of the object in hexadecimal. The code for this looks like:
// Code of Object.toString()
public String toString() {
return getClass().getName() + "#" + Integer.toHexString(hashCode());
}
A result such as com.foo.MyType#2f92e0f4 can therefore be explained as:
com.foo.MyType - the name of the class, i.e. the class is MyType in the package com.foo.
# - joins the string together
2f92e0f4 the hashcode of the object.
The name of array classes look a little different, which is explained well in the Javadocs for Class.getName(). For instance, [Ljava.lang.String means:
[ - an single-dimensional array (as opposed to [[ or [[[ etc.)
L - the array contains a class or interface
java.lang.String - the type of objects in the array
Customizing the Output
To print something different when you call System.out.println(myObject), you must override the toString() method in your own class. Here's a simple example:
public class Person {
private String name;
// constructors and other methods omitted
#Override
public String toString() {
return name;
}
}
Now if we print a Person, we see their name rather than com.foo.Person#12345678.
Bear in mind that toString() is just one way for an object to be converted to a string. Typically this output should fully describe your object in a clear and concise manner. A better toString() for our Person class might be:
#Override
public String toString() {
return getClass().getSimpleName() + "[name=" + name + "]";
}
Which would print, e.g., Person[name=Henry]. That's a really useful piece of data for debugging/testing.
If you want to focus on just one aspect of your object or include a lot of jazzy formatting, you might be better to define a separate method instead, e.g. String toElegantReport() {...}.
Auto-generating the Output
Many IDEs offer support for auto-generating a toString() method, based on the fields in the class. See docs for Eclipse and IntelliJ, for example.
Several popular Java libraries offer this feature as well. Some examples include:
ToStringBuilder from Apache Commons Lang
MoreObjects.ToStringHelper from Google Guava
#ToString annotation from Project Lombok
Printing groups of objects
So you've created a nice toString() for your class. What happens if that class is placed into an array or a collection?
Arrays
If you have an array of objects, you can call Arrays.toString() to produce a simple representation of the contents of the array. For instance, consider this array of Person objects:
Person[] people = { new Person("Fred"), new Person("Mike") };
System.out.println(Arrays.toString(people));
// Prints: [Fred, Mike]
Note: this is a call to a static method called toString() in the Arrays class, which is different to what we've been discussing above.
If you have a multi-dimensional array, you can use Arrays.deepToString() to achieve the same sort of output.
Collections
Most collections will produce a pretty output based on calling .toString() on every element.
List<Person> people = new ArrayList<>();
people.add(new Person("Alice"));
people.add(new Person("Bob"));
System.out.println(people);
// Prints [Alice, Bob]
So you just need to ensure your list elements define a nice toString() as discussed above.
I think apache provides a better util class which provides a function to get the string
ReflectionToStringBuilder.toString(object)
Every class in Java has the toString() method in it by default, which is called if you pass some object of that class to System.out.println(). By default, this call returns the className#hashcode of that object.
{
SomeClass sc = new SomeClass();
// Class # followed by hashcode of object in Hexadecimal
System.out.println(sc);
}
You can override the toString method of a class to get different output. See this example
class A {
String s = "I am just a object";
#Override
public String toString()
{
return s;
}
}
class B {
public static void main(String args[])
{
A obj = new A();
System.out.println(obj);
}
}
In Eclipse,
Go to your class,
Right click->source->Generate toString();
It will override the toString() method and will print the object of that class.
I prefer to use a utility function which uses GSON to de-serialize the Java object into JSON string.
/**
* This class provides basic/common functionalities to be applied on Java Objects.
*/
public final class ObjectUtils {
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private ObjectUtils() {
throw new UnsupportedOperationException("Instantiation of this class is not permitted in case you are using reflection.");
}
/**
* This method is responsible for de-serializing the Java Object into Json String.
*
* #param object Object to be de-serialized.
* #return String
*/
public static String deserializeObjectToString(final Object object) {
return GSON.toJson(object);
}
}
In intellij you can auto generate toString method by pressing alt+inset and then selecting toString() here is an out put for a test class:
public class test {
int a;
char b;
String c;
Test2 test2;
#Override
public String toString() {
return "test{" +
"a=" + a +
", b=" + b +
", c='" + c + '\'' +
", test2=" + test2 +
'}';
}
}
As you can see, it generates a String by concatenating, several attributes of the class, for primitives it will print their values and for reference types it will use their class type (in this case to string method of Test2).
By default, every Object in Java has the toString() method which outputs the ObjectType#HashCode.
If you want more meaningfull information then you need to override the toString() method in your class.
public class Person {
private String name;
// constructor and getter/setter omitted
// overridding toString() to print name
public String toString(){
return name;
}
}
Now when you print the person object using System.out.prtinln(personObj); it will print the name of the person instead of the classname and hashcode.
In your second case when you are trying to print the array, it prints [Lcom.foo.Person;#28a418fc the Array type and it's hashcode.
If you want to print the person names, there are many ways.
You could write your own function that iterates each person and prints
void printPersonArray(Person[] persons){
for(Person person: persons){
System.out.println(person);
}
}
You could print it using Arrays.toString(). This seems the simplest to me.
System.out.println(Arrays.toString(persons));
System.out.println(Arrays.deepToString(persons)); // for nested arrays
You could print it the java 8 way (using streams and method reference).
Arrays.stream(persons).forEach(System.out::println);
There might be other ways as well. Hope this helps. :)
If you Directly print any object of Person It will the ClassName#HashCode to the Code.
in your case com.foo.Person#2f92e0f4 is getting printed . Where Person is a class to which object belongs and 2f92e0f4 is hashCode of the Object.
public class Person {
private String name;
public Person(String name){
this.name = name;
}
// getter/setter omitted
#override
public String toString(){
return name;
}
}
Now if you try to Use the object of Person then it will print the name
Class Test
{
public static void main(String... args){
Person obj = new Person("YourName");
System.out.println(obj.toString());
}
}
If you look at the Object class (Parent class of all classes in Java) the toString() method implementation is
public String toString() {
return getClass().getName() + "#" + Integer.toHexString(hashCode());
}
whenever you print any object in Java then toString() will be call. Now it's up to you if you override toString() then your method will call other Object class method call.
Using Lombok #Data annotation on class will provide getter, setter, toString and hashcode. Using Lombok is better as it handles boilerplate code.
For a "deep" toString() there is an alternative to the JSON based answers (Jackson, GSON, etc.): ReflectionToStringBuilder from the Apache Commons Lang 3 library, with RecursiveToStringStyle or MultilineRecursiveToStringStyle. Code example:
System.out.println("My object: " +
ReflectionToStringBuilder.toString(theObject, new RecursiveToStringStyle()));
Output examples:
// RecursiveToStringStyle
Person#7f54[name=Stephen,age=29,smoker=false,job=Job#43cd2[title=Manager]]
// MultilineRecursiveToStringStyle
Person#7f54[
name=Stephen,
age=29,
smoker=false,
job=Job#43cd2[
title=Manager
]
]
I managed to get this done using Jackson in Spring 5. Depending on the object it might not work in all cases.
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(yourObject));
the output would look like
{
"id" : 1,
"fieldOne" : "string"
}
Here are more examples using Jackson
If you use GSON instead It might look like
Gson gson = new Gson();
System.out.println(gson.toJson(yourObject));
If you are using project Lombok you could use the #ToString annotation and generate a standard toString() method without adding boilerplate.
import lombok.ToString;
#ToString
public class LoginDto {
private String user;
private String pass;
}
...
System.out.println(loginDto.toString());
// LoginDto(user=x#xxx.x, pass=xxxxx)

#Order annotation has no effect on the XML serialization order

I'm using Retrofit 2 with a SimpleXmlConverter and I am facing an issue when creating a Soap Request Object, that is basically an element with 4 element children each one of them being different datatypes.
Here is the XML output I want to produce. The element order must be respected:
<prf:container>
<prf:aaa>111111111</prf:aaa>
<prf:bbb>true</prf:bbb>
<prf:element>
<prf:ddd>50</prf:ddd>
<prf:eee>false</prf:eee>
</prf:element>
<prf:ccc>textcontent</prf:ccc>
</prf:container>
Now, here is my Android Class, Container.java, representing the Soap Request Object that will be serialized:
#Root (name = "prf:container")
#Order(elements={"prf:aaa", "prf:bbb", "prf:element", "prf:ccc"})
public class Container {
#Element (name = "prf:aaa")
private int aaa;
#Element(name = "prf:bbb")
private boolean bbb;
#Element (name = "prf:element", required = false)
private MyElement myElement;
#Element (name = "prf:ccc", required = false)
private String ccc;
}
According to the Simple XML framework documentation:
By default serialization of fields is done in declaration order.
However, in Android, this is not true, at least in some cases. No matter how I set the field declaration order in my Container class, the output has always the same element order. This is a known bug and as has been reported in other SO posts.
Nonetheless, there is a solution to this issue. The Order annotation.
Read more in the Javadoc.
My problem is that using the Order annotation in my case is not helping. Note that all my elements have a prefix on its name - prf:.
If I remove the prf prefix from all my element names, Order annotation will work properly, and force the XML Serialization to have the defined order. But the output elements won't have the prefix on its name.
But I really need my elements to have the prefix on its name, or else my request will have a 500 response. I also have to have the desired element order in my XML output.
Any solution to this?
Thank you
I know it has been a long item since you posted this question but, I would like to answer your question in case anyone faced the same issue. I solved the same issue by doing the following:
For the XML document to be prepared with the elements in the order you want and if the elements have a prefix, #Order annotation might not work in some cases. In your case, the prefix 'prf' mentioned in the #Order annotation for each element would not work to order them as you desired.
"By default serialization of fields is done in declaration order."
I don't believe this either, especially when you have prefixes for elements. So, I tried changing the Java variable names. I tried naming them in alphabetical order in the same way I needed them in the generated xml. So, in your case, you can change the variable names as follows:
#Root (name = "prf:container")
public class Container {
#Element (name = "prf:aaa")
private int element1;
#Element(name = "prf:bbb")
private boolean element2;
#Element (name = "prf:element", required = false)
private MyElement element3;
#Element (name = "prf:ccc", required = false)
private String element4;
}
This would form the xml document exactly as you wanted. You might wonder that if we change the variable names to be too generic, they are not representing what they actually are but, you can always have getters and setters. For example, in your case you can have:
public void setAaa(String aaa){
this.element1 = aaa;
}
public String getAaa(){
return element1;
}
In the same way you can always generate the classes with alphabetically ordered variables to make sure the generated xml has the elements in the desired format.
Maybe you using #Order with wrong syntax,Alphabetical order is not important. You can try:
#Root (name = "prf:container")
#Order(elements={"prf:container/prf:aaa", "prf:container/prf:bbb", "prf:container/prf:element", "prf:container/prf:ccc"})
public class Container {
#Element (name = "prf:aaa")
private int aaa;
#Element(name = "prf:bbb")
private boolean bbb;
#Element (name = "prf:element", required = false)
private MyElement myElement;
#Element (name = "prf:ccc", required = false)
private String ccc;
}
SimpleXML's auto ordering by alphabetical order is working. But on one condition: the type of those fields should be the same, usually for XML it is String. It took me long time to figure that out, I had different types, and ordering by name didn't work. Since I've changed all fields to String works like a charm.
#Root(name = "sch:CheckPaymentRequest", strict = true)
public class CheckPaymentData {
#Element(name = "sch:payId")
private String Aaa1;
#Element(name = "sch:fromCurrency")
private String Bbb2;
#Element(name = "sch:fromAmount")
private String Ccc3;
...}

How to use Spatialite with Xamarin on Android

I want to use Spatialite instead of plain SQLite with Xamarin on Android, to manage and display geographical data. Built-in SQLite does not allow to load extensions. How can I do it?
Short answer: you need to use own customized SQLite as Android Native Library, like with other NDK libraries. The tricky part is to get useful not so trivial C# API for the database. Xamarin docs seems to have guides for very simple single method APIs only.
As I am way more familiar to Java than .Net, then I used combination of Android Java library (.jar) and Android native library (.so). Android Java library has already Java API wrapper for the database, it is exactly the same wrapper as can be used in the usual Android Java applications. Of course, technically direct access of the native library from C# would be also possible, so java/jar could be excluded from the story. If you know good tools for that, let me know also.
Create .jar binding project for Xamarin, add it to the same solution as your Android project
Add jsqlite.jar to Jars folder of the bindings project. Get it from here: jsqlite.jar
Add native library binaries (libjsqlite.so and libproj.so) to your application project, create folder libs/armeabi for this. Get these from Nutiteq AdvancedMap3D project
Define the .so files as AndroidNativeLibrary, and set Copy to Output Directory
Fix binding definitions to remove build errors. Add following to Transforms/Metadata.xml of your bindings project:
<remove-node path="/api/package[#name='jsqlite']/class[#name='Backup']/field[#name='handle']" />
<remove-node path="/api/package[#name='jsqlite']/class[#name='Database']/field[#name='handle']"/>
<attr path="/api/package[#name='jsqlite']" name="managedName">jsqlite</attr>
This should generate you working C# API to bundled SQLite, together with Spatialite, Proj.4 and GEOS included. The jsqlite DB API itself is different from other C# SQLite APIs, you need to use callback classes. See following examples To check versions of the modules:
try {
db.Open ("/sdcard/mapxt/estonia-latest-map.sqlite", Constants.SqliteOpenReadonly);
// show versions to verify that modules are there
db.Exec ("SELECT spatialite_version(), proj4_version(), geos_version(), sqlite_version()", new GeneralQryResult ());
} catch (jsqlite.Exception ex) {
Log.Error( ex.LocalizedMessage );
}
...
// prints query results as text
public class GeneralQryResult : Java.Lang.Object, ICallback
{
public bool Newrow (string[] rowdata)
{
string row = "";
foreach (var data in rowdata) {
row += data + " | ";
}
Log.Info(row);
return false;
}
public void Types (string[] types)
{
// never called really
}
public void Columns (string[] cols){
Log.Debug ("Query result:");
string row = "";
foreach (var col in cols) {
row += col + " | ";
}
Log.Info (row);
}
}
Finally now a query of real spatial data, using Nutiteq 3D Maps SDK for Xamarin to visualize it:
// Spatialite query, show results on map
// 1. create style and layer for data
LineStyle.Builder lineStyleBuilder = new LineStyle.Builder ();
lineStyleBuilder.SetColor (NutiteqComponents.Color.Argb(0xff, 0x5C, 0x40, 0x33)); //brown
lineStyleBuilder.SetWidth (0.05f);
LineStyle lineStyle = lineStyleBuilder.Build ();
GeometryLayer geomLayer = new GeometryLayer (view.Layers.BaseLayer.Projection);
view.Layers.AddLayer (geomLayer);
// 2. do the query, pass results to the layer
Database db = new Database ();
try {
db.Open ("/sdcard/mapxt/estonia-latest-map.sqlite", Constants.SqliteOpenReadonly);
// spatial query. Limit to 1000 objects to avoid layer overloading
String qry = "SELECT id, HEX(AsBinary(Transform(geometry,3857))), sub_type, name FROM ln_railway LIMIT 1000";
db.Exec (qry, new SpatialQryResult (geomLayer, lineStyle));
} catch (jsqlite.Exception ex) {
Log.Error( ex.LocalizedMessage );
}
...
// adds query results to given layer, with given style
public class SpatialQryResult : Java.Lang.Object, ICallback
{
GeometryLayer _geomLayer;
Style _geomStyle;
public SpatialQryResult(GeometryLayer geomLayer, Style geomStyle){
_geomLayer = geomLayer;
_geomStyle = geomStyle;
}
public bool Newrow (string[] rowdata)
{
string id = rowdata [0];
string geomHex = rowdata [1];
string type = rowdata [2];
string name = rowdata [3];
Label label;
if (name != null && name.Length > 1) {
label = new DefaultLabel (name, type);
} else {
label = null;
}
Geometry[] lineGeoms = WkbRead.ReadWkb(new ByteArrayInputStream(Utils
.HexStringToByteArray(geomHex)), rowdata);
// following fails if not Line, change for other geometries
foreach (Line lineGeom in lineGeoms) {
_geomLayer.Add(new Line(lineGeom.VertexList, label, (LineStyle)_geomStyle, _geomLayer));
}
return false;
}
}

#interface - What?

I know what interfaces are, but I don't know enough about java to know how to search for my answer. So what does this mean and do:
public #interface ThreadSafe { }
// different file
#ThreadSafe
public class Model {
What does this line mean/do "#interface"? What does it mean/do when it's applied above "#ThreadSafe"
This is called an annotation. You may want to look at the Java tutorial. It has a pretty good explanation of what they are and how they are used.
https://docs.oracle.com/javase/tutorial/java/annotations/index.html
Many annotations replace comments in code.
Suppose that a software group traditionally starts the body of every class with comments providing important information:
public class Generation3List extends Generation2List {
// Author: John Doe
// Date: 3/17/2002
// Current revision: 6
// Last modified: 4/12/2004
// By: Jane Doe
// Reviewers: Alice, Bill, Cindy
// class code goes here
}
To add this same metadata with an annotation, you must first define the annotation type. The syntax for doing this is:
#interface ClassPreamble {
String author();
String date();
int currentRevision() default 1;
String lastModified() default "N/A";
String lastModifiedBy() default "N/A";
// Note use of array
String[] reviewers();
}
The annotation type definition looks similar to an interface definition where the keyword interface is preceded by the at sign (#) (# = AT, as in annotation type). Annotation types are a form of interface, which will be covered in a later lesson. For the moment, you do not need to understand interfaces.
The body of the previous annotation definition contains annotation type element declarations, which look a lot like methods. Note that they can define optional default values.
After the annotation type is defined, you can use annotations of that type, with the values filled in, like this:
#ClassPreamble (
author = "John Doe",
date = "3/17/2002",
currentRevision = 6,
lastModified = "4/12/2004",
lastModifiedBy = "Jane Doe",
// Note array notation
reviewers = {"Alice", "Bob", "Cindy"}
)
public class Generation3List extends Generation2List {
// class code goes here
}
Source: https://docs.oracle.com/javase/tutorial/java/annotations/declaring.html

Categories

Resources