Monday, August 1, 2005

Remove duplicate and unused entries from your property files

WARNING: This blog entry was imported from my old blog on blogs.sun.com (which used different blogging software), so formatting and links may not be correct.


Three years ago or so I realized the code I was working on had lots of "stale" Bundle.properties entries. These are bad because not only do they create more work for localizers who translate these files to other languages, but unused keys also incur more runtime overhead.



It's easy to end up with unused key entries. Perhaps you first comment out some code which is referencing a bundle key - but you don't comment out the bundle key. Later the commented out code is removed, but the key in the property file is still there.



To find these I
wrote a really hairy Unix command; something like this:



find . -name Bundle.properties -exec cat {} \; | /bin/grep "=" | sed
'/^#/d' | sed '/^OpenIDE-Module-/d' | nawk 'BEGIN { FS="=" } {print $1}'
| sort | uniq | nawk '{system("/bin/fgrep " $0 " `find . -name
\"*.class\" -o -name \"*.xml\"` \| nawk \"BEGIN \{i=0\} \{ i++ \} END \{
if \(i==0\) print key \}\" key=" $0)}' | /bin/xargs -n 1 -I @ grep @
`find . -name Bundle.properties`


I recently figured it was time to check my Bundle files in my current code - so I used a tool I had seen
Tim Boudreau commit into NetBeans CVS. It's a standalone tool, not
integrated as part of the IDE. But it's quite useful.



The tool is called "Bundlizer", and is found in NetBeans CVS under contrib/bundlizer.
Just load it as a project in NetBeans and run it and you'll get the GUI (see screenshot below).
Or, use the executable jar I just uploaded here. Use at your own risk.



The program does a couple of useful things:


  • It looks for duplicates: the same key used more than once. You are alerted to this specifically since you'll need
    to decide which value to keep.

  • It looks for unused keys. These are removed from the file.

  • It cleans up formatting in the file, removing empty lines etc.


When you're done it saves the resulting file.



You'll definitely want to check its results. Discovering if a key is unused is not something which can be done
accurately. Obviously, if your code is doing something fancy, like computing keys rather than looking them
up directly, the program will not figure that out and will think the keys are unused. One common case of that in my
own code had to do with keys that are referenced from XML files. For example, NetBeans plugin code declares menu items
in an XML file, and these pull keys out from a bundle. There are other ways the program can be tricked too.



Consider yourself warned. However, the program is extremely useful at identifying candidates for removal. You'll
probably see some keys in your own files and go "oooooh yeah.... that's still there? Guess I forgot to remove it!".



Here's a screenshot of the utility in action (click on it for full size):








I suspect the reason the utility has not been "finished" and moved into the IDE as part of the Resource Bundle support,
is precisely because discovering unused keys is such an inaccurate science. If anyone would like to take a closer
look at the code and try to make it smarter, please do!
I made a couple of tweaks for my own purposes before running the tool. Here's the patch I applied -
the first diff makes it look in XML files for key references; the second makes the tool less unhappy
when it encounters non-comment lines that don't contain and = sign:


Index: contrib/bundlizer/src/org/netbeans/modules/bundlizer/BundlizerPanel.java
===================================================================
RCS file: /cvs/contrib/bundlizer/src/org/netbeans/modules/bundlizer/BundlizerPanel.java,v
retrieving revision 1.3
diff -u -r1.3 BundlizerPanel.java
--- contrib/bundlizer/src/org/netbeans/modules/bundlizer/BundlizerPanel.java 7 May 2004 23:48:40 -0000 1.3
+++ contrib/bundlizer/src/org/netbeans/modules/bundlizer/BundlizerPanel.java 2 Aug 2005 05:45:46 -0000
@@ -449,7 +449,7 @@

private class JavaFilenameFilter implements java.io.FilenameFilter {
public boolean accept(java.io.File file, String str) {
- return str.endsWith ("java");
+ return str.endsWith ("java") || str.endsWith("xml");
}
}

Index: contrib/bundlizer/src/org/netbeans/modules/bundlizer/Properties.java
===================================================================
RCS file: /cvs/contrib/bundlizer/src/org/netbeans/modules/bundlizer/Properties.java,v
retrieving revision 1.2
diff -u -r1.2 Properties.java
--- contrib/bundlizer/src/org/netbeans/modules/bundlizer/Properties.java 1 May 2004 13:52:24 -0000 1.2
+++ contrib/bundlizer/src/org/netbeans/modules/bundlizer/Properties.java 2 Aug 2005 05:45:46 -0000
@@ -115,6 +115,10 @@
}
String keyValuePair = line;
int equals = keyValuePair.indexOf ("=");
+ if (equals == -1) {
+ continue;
+ }
assert equals != -1;

String key = keyValuePair.substring (0, equals).trim();
@@ -203,6 +207,9 @@

private boolean isKeyValuePair (String line) {
int equals = line.indexOf("=");
+ if (equals == -1) {
+ return false;
+ }
int cmt = line.indexOf ("#");
return (cmt > 0 && equals < cmt) || (cmt < 0 && equals >= 0);
}


1 comment:

  1. Why don't you just apply the patches you list here?

    ReplyDelete