Compile time resource checking (like in Android) for J2SE Development

83 views
Skip to first unread message

Sam Reid

unread,
Jun 22, 2011, 2:01:41 PM6/22/11
to The Java Posse
Hi Java Posse People,

I've found the automatic generation of R.java in android development
to be a significant improvement over the way we usually load resources
in our J2SE applications, attaining benefits like compile time
checking for missing resources. Does anyone know of a tool or
technique for doing something like this in J2SE?

Thanks!
Sam

Ricky Clarkson

unread,
Jun 22, 2011, 3:08:52 PM6/22/11
to java...@googlegroups.com
For what it's worth, I try to load all language keys at least when the class is loaded, i.e., assigning them all to static finals, and then have a LoadAllClassesTest that goes over the classpath and attempts to load all classes.

Not quite the same, but probably catches most of the same bugs.  It could be adapted to other kinds of resource, but perhaps you wouldn't want to load all images eagerly.


--
You received this message because you are subscribed to the Google Groups "The Java Posse" group.
To post to this group, send email to java...@googlegroups.com.
To unsubscribe from this group, send email to javaposse+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/javaposse?hl=en.


Fabrizio Giudici

unread,
Jun 22, 2011, 3:37:21 PM6/22/11
to java...@googlegroups.com, Ricky Clarkson
On 06/22/2011 09:08 PM, Ricky Clarkson wrote:
> For what it's worth, I try to load all language keys at least when the
> class is loaded, i.e., assigning them all to static finals, and then
> have a LoadAllClassesTest that goes over the classpath and attempts to
> load all classes.
>
> Not quite the same, but probably catches most of the same bugs. It
> could be adapted to other kinds of resource, but perhaps you wouldn't
> want to load all images eagerly.
It's also a matter of compactness of code. With standard bundles and a
bit of syntactic sugar one can write (example from my code):
|
view.notifyFeedUnavailable(notification().withCaption(_,
"unavailableNewsTitle")
.withText(_,
"unavailableNewsMessage"));|

I would prefer to avoid adding two lines for declaring two fields (even
though I agree that it's important to shrink code in the body of
methods, less important in field declarations). I'm with Sam and I would
like to see:
|
view.notifyFeedUnavailable(notification().withCaption(R.string.unavailableNewsTitle)

.withText(R.string.unavailableNewsMessage));|

Note that this would be slightly better than Android R - those plain int
constants need casts everywhere - it has been discussed here in past,
and Google's version is that plain integer constants allow faster code -
reasonable thinking of 2008, maybe not in 2011. In any case, it wouldn't
be reasonable in J2SE. So, if somebody is going to do that, please use a
special Key<T> object which would avoid casts.

In any case, it shouldn't be too complex to do that. Android has to
compile resources in a binary format that is efficient at runtime in the
device, while in J2SE we just need a wrapper on existing library classes
such as ResourceBundle or NbBundle. Probably a Maven plugin for doing
that would be quite simple.

--
Fabrizio Giudici - Java Architect, Project Manager
Tidalwave s.a.s. - "We make Java work. Everywhere."
java.net/blog/fabriziogiudici - www.tidalwave.it/people
Fabrizio...@tidalwave.it

Casper Bang

unread,
Jun 23, 2011, 4:58:27 AM6/23/11
to The Java Posse
I do a similar thing; have a lightweight SQL resource utility
QueryMap, which takes resource(s) full of queries and puts them in a
map identified by the comment right in front of it:

-- ALL_CUSTOMERS
select
...
from
...
where
...
;

QueryMap q = new QueryMap("customer.sql);
q.get("ALL_CUSTOMERS");

A simple NetBeans plugin is then able to find my customer.sql, and in
the get accessor, provide me a list of available queries, required
bind variables along with showing the SQL in the popup. Admittedly the
main motivation behind this is to allow users to manipulate queries in
their favorite SQL tool, whereas on Android the main motivation seems
to be a desire to encapsulate (and package) dynamic resources
statically.

I'm torn on the Android R generator running behind, on one hand I
understand its purpose (this is how it works in .NET), on the other
hand I am not certain the folloing code:

Drawable image = getResources().getDrawable(R.drawable.myimage);
String mystring = this.getString(R.string.mystring);
ImageView imageView = (ImageView) findViewById(R.id.myimageview);

...is more consistent, more type-safe or as mockable as:

Drawable image = getResources().get(Drawable.class, "myimage")
String mystring = getResources().get(String.class, "mystring");
ImageView imageView = findViewById(imageView.class,"myimageview");

Let the IDE do code completion and the compiler do validation and
optimization.

/Casper

Moandji Ezana

unread,
Jun 23, 2011, 5:20:26 AM6/23/11
to java...@googlegroups.com
On Wed, Jun 22, 2011 at 9:37 PM, Fabrizio Giudici <fabrizio...@tidalwave.it> wrote:
I would prefer to avoid adding two lines for declaring two fields (even though I agree that it's important to shrink code in the body of methods, less important in field declarations). I'm with Sam and I would like to see:
|
view.notifyFeedUnavailable(notification().withCaption(R.string.unavailableNewsTitle)
                                        .withText(R.string.unavailableNewsMessage));|

Note that this would be slightly better than Android R - those plain int constants need casts everywhere - it has been discussed here in past, and Google's version is that plain integer constants allow faster code - reasonable thinking of 2008, maybe not in 2011. In any case, it wouldn't be reasonable in J2SE.

Wouldn't this just be a matter of reading properties files and generating a R.string entry for each property? 
 
So, if somebody is going to do that, please use a special Key<T> object which would avoid casts.

Why not just make it a string, if it's in R.string ?

Moandji

Fabrizio Giudici

unread,
Jun 23, 2011, 5:46:25 AM6/23/11
to java...@googlegroups.com, Moandji Ezana
On 06/23/2011 11:20 AM, Moandji Ezana wrote:
>
> So, if somebody is going to do that, please use a special Key<T>
> object which would avoid casts.
>
>
> Why not just make it a string, if it's in R.string ?
>
>
Well, right. I depends on how precisely one wants to reproduce the stuff
in Android. Indeed, some string could be very long (e.g. a small html
page?) and you might want to lazy load it.

Reinier Zwitserloot

unread,
Jun 23, 2011, 6:21:52 AM6/23/11
to java...@googlegroups.com
GWT has another somewhat interesting tactic; you write an interface containing one method named after an image file for each image. GWT has a utility class that gives you an instance of the same type as the interface, and all methods just work.

You could build something very similar using an annotation processor (You'd generate the actual implementing class, based on the interface, which has an annotation to trigger the AP). You'd then get compile checking in both cases as well as auto-complete on your resources (i.e. you'll get an error at compile time if an image is missing because the AP can tell, and they can raise a compiler error, and you'll get an error if some other code tries to use a non-existing image because the method won't exist in the interface).

Another option is that your interface is just an empty, package-private dummy whose only purpose is to carry an annotation that includes a path to a directory of images. This AP can then generate the entire interface + implementation (or just bundle it up in one class if you prefer) with 1 method for each resource that it found in the file.

I doubt this would take you more than a single man-day of work, including testing.

Moandji Ezana

unread,
Jun 23, 2011, 7:14:33 AM6/23/11
to java...@googlegroups.com
On Thu, Jun 23, 2011 at 12:21 PM, Reinier Zwitserloot <rein...@gmail.com> wrote:
I doubt this would take you more than a single man-day of work, including testing.

It'd take me a bit longer than a day, but not that much longer. So why it hasn't been done already? For client-side applications at least, it makes a lot of sense.

Moandji

Casper Bang

unread,
Jun 23, 2011, 7:26:23 AM6/23/11
to The Java Posse
> So why it hasn't been done already?

Because it's not very Java'sque to use code generation tools and an
official pluggable annotation processor only arrived with JSE6? It's
might have been a nice extension to JSR-296 but alas, Sun wanted to
push JavaFX instead.

Fabrizio Giudici

unread,
Jun 23, 2011, 7:41:26 AM6/23/11
to java...@googlegroups.com, Casper Bang
Hey, there's a thing named community. While it could be done with an
annotation processor, it could be just done with a much simpler Maven
plugin. Too bad this week for me is hot, otherwise I'd seriously start
thinking of it (considering that I use NetBeans NbBundle in place of
ResourceBundle, and it's unlikely that if something exists, it supports
NbBundle).

Kevin Wright

unread,
Jun 23, 2011, 8:02:00 AM6/23/11
to java...@googlegroups.com, Casper Bang
Before everyone here gets too far ahead of themselves, can we please stop over-engineering.

It looks like we simply need to generate a .java file that contains a single class with static final members, each one corresponding to a file in some directory (presumably something like src/main/resources/images that's guaranteed to be JAR'd and visible on the classpath at runtime)

The actual code generation will be a trivial "7 lines in your favourite scripting language" sort of affair, so let's call it 20-30 lines in Java (i.e. still not exactly massive).  Just to be safe, you'll want it to be regenerated as an automatic part of your build process, so do whatever it takes to turn into an ant/maven/gradle/rake/sbt/whatever task.  Sure, this'll add a lump more boilerplate, but at least you can find templates (a.k.a. pre-written lumps of boilerplate) for that kind of thing; so the pain level remains low.


I know the trend in Java is to avoid static typing like the plague (think of Spring's XML configuration files, or anything that uses strings in annotations heavily), but I'm really at a loss to see why this problem needs property files, annotation processors, deep IDE integration, or anything else of that nature.



On 23 June 2011 12:41, Fabrizio Giudici <fabrizio...@tidalwave.it> wrote:
On 06/23/2011 01:26 PM, Casper Bang wrote:
So why it hasn't been done already?
Because it's not very Java'sque to use code generation tools and an
official pluggable annotation processor only arrived with JSE6? It's
might have been a nice extension to JSR-296 but alas, Sun wanted to
push JavaFX instead.
Hey, there's a thing named community. While it could be done with an annotation processor, it could be just done with a much simpler Maven plugin. Too bad this week for me is hot, otherwise I'd seriously start thinking of it (considering that I use NetBeans NbBundle in place of ResourceBundle, and it's unlikely that if something exists, it supports NbBundle).



--
Kevin Wright

gtalk / msn : kev.lee...@gmail.com
mail: kevin....@scalatechnology.com
vibe / skype: kev.lee.wright
quora: http://www.quora.com/Kevin-Wright
twitter: @thecoda

"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra

Moandji Ezana

unread,
Jun 23, 2011, 8:05:30 AM6/23/11
to java...@googlegroups.com
On Thu, Jun 23, 2011 at 1:26 PM, Casper Bang <caspe...@gmail.com> wrote:
Because it's not very Java'sque to use code generation tools and an
official pluggable annotation processor only arrived with JSE6?

I guess with JPA 2's typed criteria queries, this kind of codegen is fairly well-accepted and, along with Android's R, clearly quite useful.

I don't really agree with the need for annotations, because the point is to statically type something that is originally plain text. If you have to write a class to annotate it, you might as well write the whole thing (unless you can use package-level annotations to say where the resource files are). I guess there could be several entry points to the core functionality (annotations, Maven, directly specifying input and output folders...).

Moandji

Kevin Wright

unread,
Jun 23, 2011, 1:45:33 PM6/23/11
to java...@googlegroups.com

Anyways, since our project uses an unconventional way of storing and
loading resources, this may be of limited reusability for others, but
in case you are interested, you can view my solution in our
repository:

Resource generator: http://goo.gl/QDNxG
Example generated file: http://goo.gl/k8qNI

The repository is not fully public so to log in or checkout you have
to use username and password = guest


What's the licencing on that code? It's given me an idea...

Cédric Beust ♔

unread,
Jun 23, 2011, 1:50:07 PM6/23/11
to java...@googlegroups.com
On Thu, Jun 23, 2011 at 1:58 AM, Casper Bang <caspe...@gmail.com> wrote:

I'm torn on the Android R generator running behind, on one hand I
understand its purpose (this is how it works in .NET), on the other
hand I am not certain the folloing code:

Drawable image = getResources().getDrawable(R.drawable.myimage);
String mystring = this.getString(R.string.mystring);
ImageView imageView = (ImageView) findViewById(R.id.myimageview);

...is more consistent, more type-safe or as mockable as:

Drawable image = getResources().get(Drawable.class, "myimage")
String mystring = getResources().get(String.class, "mystring");
ImageView imageView = findViewById(imageView.class,"myimageview");

It seems pretty straightforward to add an abstraction that sits on top of the R files and that offers you the API above.

I think Android's R system is probably the best that can be done today in Java from a "type checked XML" standpoint.

Ideally, something like Gosu's open type system would probably be my choice: no more code generation, just expose classes that represent the resources in a statically typed way.

-- 
Cédric

Sam Reid

unread,
Jun 23, 2011, 1:32:14 PM6/23/11
to The Java Posse
I went ahead and wrote a tool for our project to perform resource
generation for our project, and used it to generate image and string
resources for one of our applications.

Kevin Wright said:
> The actual code generation will be a trivial "7 lines in your favourite scripting language" sort of affair, so let's call it 20-30 lines in Java (i.e. still not exactly massive).

My solution is about 130 lines of Java, plus a 20 line java source
template, and it handles internationalizable strings as well as
images. It doesn't auto-scan for changes and regenate (I have to
double click to launch a script), but that didn't make it much less
useful, and that feature could be added later (maybe sbt be good at
auto-scan). It still helped me identify unused images and text in my
project and guarantees that references strings and images won't be
missing at runtime.

Fabrizio Giudici said:
>So, if somebody is going to do that, please use a special Key<T> object which would avoid casts.

My implementation only handles Strings and BufferedImages which are
eagerly loaded with the correct types.

Reinier Zwitserloot said:
> I doubt this would take you more than a single man-day of work, including testing.

It took me about 1.75 hours to implement this, and use it to load
images and strings for one of our projects, and to use it to
internationalize the project.

Casper Bang said:
> So why it hasn't been done already?

After working on this, I think it is successful in Android because
they were able to choose a standard convention for where/how resources
go and how they are loaded, and they provided the tool set so that it
works right out of the box. My project uses its own (different)
standard--I'm guessing there is more standardization with maven-like
conventions, but other projects may have different conventions, which
would inhibit the universality of a J2SE tool like this (or maybe
encourage standardization on maven-like convention).

Anyways, since our project uses an unconventional way of storing and
loading resources, this may be of limited reusability for others, but
in case you are interested, you can view my solution in our
repository:

Resource generator: http://goo.gl/QDNxG
Example generated file: http://goo.gl/k8qNI

The repository is not fully public so to log in or checkout you have
to use username and password = guest

Some outstanding issues with my particular implementation:
1. String and image names may collide--we'll probably have to handle
that at some point. Maybe I will split them into separate generated
java source files, or prefix images fields with "IMAGE_" (that
wouldn't guarantee collision-free fields, just increase the
probability).
2. My implementation doesn't yet handle substructure in images
directory
3. No good support yet for for strings that have to be formatted with
MessageFormatter
4. No support for audio yet or other resource types.

Thanks for your comments and ideas!
Sam

Sam Reid

unread,
Jun 23, 2011, 2:18:58 PM6/23/11
to The Java Posse
The blanket license for our project is GPL:
http://phet.colorado.edu/en/about/licensing

However, I think this component of our build tools is unencumbered and
could probably be relicensed as LGPL if that's better for you--let me
know if so and I'll bring it up at our next meeting.

Also, I replied using the forum interface on the groups webpage here:
http://groups.google.com/group/javaposse/browse_thread/thread/e5103d97671d73a5
and haven't seen my message appear in the forum yet, but since you
quoted it in a response I guess it appeared over your email? Maybe
google groups is having a weird hiccup. I subscribed for emails and
if I don't see my original message appear again soon, I may repost it
(unless someone knows what's happening here--does it need to be
approved by a moderator or something?)

Cédric Beust ♔

unread,
Jun 23, 2011, 2:24:13 PM6/23/11
to java...@googlegroups.com
I strongly suggest using email, the web interface is not just flaky, it has a tendency to strip the context, which makes it impossible for readers to see who you are responding to.

-- 
Cédric




Kevin Wright

unread,
Jun 23, 2011, 2:55:52 PM6/23/11
to java...@googlegroups.com
On 23 June 2011 19:18, Sam Reid <samr...@gmail.com> wrote:
The blanket license for our project is GPL:
http://phet.colorado.edu/en/about/licensing

However, I think this component of our build tools is unencumbered and
could probably be relicensed as LGPL if that's better for you--let me
know if so and I'll bring it up at our next meeting.


That's okay, it's simple enough to re-implement from scratch with an ASL or BSD license.

Fabrizio Giudici

unread,
Jun 24, 2011, 3:00:16 AM6/24/11
to java...@googlegroups.com, Sam Reid
On 06/23/2011 07:32 PM, Sam Reid wrote:
> I went ahead and wrote a tool for our project to perform resource
> generation for our project, and used it to generate image and string
> resources for one of our applications.
Thanks for sharing. I'll look at it in the weekend - you made me wanting
to have a similar thing for NbBundle...

Reinier Zwitserloot

unread,
Jun 24, 2011, 10:27:43 AM6/24/11
to java...@googlegroups.com, Casper Bang
Annotation Processors are the simplest way to have something that will be picked up as part of the compile process. *ANY* compile process. Your IDE, maven, ant, ivy, gradle, etc with 0 conf. Using an AP is using the right tool for the job. Writing an ant plugin or an elaborate task is overengineering

Op donderdag 23 juni 2011 14:02:00 UTC+2 schreef KWright het volgende:
Before everyone here gets too far ahead of themselves, can we please stop over-engineering.

Reply all
Reply to author
Forward
0 new messages