ResourceCheck

Description

Internationalization in Java programs is done via resourcebundles. In the Java source are no textes like "Delete" for a push button but the name of the resourcebundle key (e.g. pushbutton.delete). In the english resourcebundle the key pushbutton.delete than has the value "Delete", in the german resourcebundle the key would have the value "Löschen". When building the software for the customers these resourcebundles should be checked for correctnes. If there are errors in a resourcebundle the program would be display incorrect or even fail to start.

This task checks for existence of all keys in each bundle, duplicate keys, existence of forbidden chars (e.g. umlaute) and continuous usage of placeholders.

The Task is hosted on sourceforge.net http://sourceforge.net/projects/rscbundlecheck

Parameters

Attribute Description Required
failOnError Flag if the task should throw an exception on error (stop the build process).
If set to false all errors found will only loged to console (warn).
No
sortResult Flag if the result should be sorted. No
verbose Verbose output.  No

Parameters specified as nested elements

fileset

A collection of resourcebundles to be checked

checks

A collection of checks to be executed

Parameters

Attribute Description Required
include Check to be included, the special name "*" means "all predefined checks".
No
exclude Check to be excluded.  No


The following checks are available and can be used without additional parameters:

Name
Description
unicode check Checks if unicodes are well-formed and valid
upper lower check Checks if lines across the bundles have consistent cases, e.g "the Street invalid" vs. "Die Strasse ungültig"
line end check Checks each line for trailing whitespaces/tabs
empty key check Checks if there are empty key ("= value without a key")
placeholder check Checks if placeholders are used consistent (e.g. "Got {0}, expected {1}" vs. "Erhalten {0}")
duplicate key check Checks if a key is defined more than once in one file
empty value check Checks if there are empty values ("key without a value =")
invalid char check Checks if a value has invalid characters (ASCII value > 127)
messageformat check Checks if MessageFormat can be instantiated  with each value
cross bundle check Checks if each key is found in all bundles


The following checks are available also but need some parameters (check the "key regexp check" example below how to set them):

Name Description Parameters (bold=mandatory)
key regexp check Checks each key if it matches the allowedKeyRegexp pattern
allowedKeyRegexp
allowed char key check Checks each key if it contains only chars passed as allowedKeyChars and or/if it does not contain charactes found in disallowedKeyChars allowedKeyChars, disallowedKeyChars (one must set)
unused key check
This task's most exciting feature: Checks if all keys refered from Java exist and/or if there are keys defined in the bundles but not used in the Java Code.
All classes found in the set classpath (jars/directories conatining the compiled bytecode, multiple entries seperated by File.pathSeparator) are inspected whether they call the method nlsMethodName (e.g. "com.example.ResourceHelper#getText(java.lang.String)").
You can pass multiple methods seperated by ":" e.g. "com.example.ResourceHelper#getText(java.lang.String): com.example.ResourceHelper#getText(java.lang.String, java.lang.String)"

classpath, nlsMethodName,
searchNotExistingKeys
searchUnusedKeys


Examples

The following example will check all resoucebundles named "resourcebundle*.properties" found in directory "${client.base}/resource" and stop the build process on errors. 

<target name="default">
<taskdef classpath="path/to/rscbundlecheck-bin.jar" resource="task.properties"/>
<resourceCheck>
<fileset dir="${client.base}/resource">
<include name="resourcebundle*.properties"/>
</fileset>
</resourceCheck>
</target>

The following example will check all resoucebundles named "resourcebundle*.properties" found in directory "${client.base}/resource" only for continous usage of upper-/lowercase at the values' first characters but do not stop the build process on erros (warn only).

<target name="default">
<taskdef classpath="path/to/rscbundlecheck-bin.jar" resource="task.properties"/>
<resourceCheck failonerror="false">
<fileset dir="${client.base}/resource">
<include name="resourcebundle*.properties"/>
</fileset>
<checks>
<include name="upper lower check"/>
</checks>
</resourceCheck>
</target>

The following example will check all resoucebundles named "resourcebundle*.properties" found in directory "${client.base}/resource" with all predefined checks and additonally check if all resourcebundle keys start with "Txt" and contains only letters a-z or A-Z. 

<target name="default">
<taskdef classpath="path/to/rscbundlecheck-bin.jar" resource="task.properties"/>
<resourceCheck failonerror="false">
<fileset dir="${client.base}/resource">
<include name="resourcebundle*.properties"/>
</fileset>
<checks>
<include name="*"/>
<include name="key regexp check">
<arguments>
<argument name="allowedKeyRegexp" value="Txt[a-zA-Z]+"/>
</arguments>
</include>
</checks>
</resourceCheck>
</target>

Of course you can combine all the examples: Start to check for errors and stop and after that check for softer problems and warn only.

Writing your own check (extending RscBundleCheck)

You can easily extend the resourcebundlecCheck using your own check class.

<target name="default">
<taskdef classpath="path/to/rscbundlecheck-bin.jar:another/class/path" resource="task.properties"/>
<resourceCheck failonerror="false">
<fileset dir="${client.base}/resource">
<include name="resourcebundle*.properties"/>
</fileset>
<checks>
<include name="*"/>
<include classname="com.example.MyVisitor"/>
</checks>
</resourceCheck>
</target>

Check all matching resourcebundles using all the default checks plus your own check defined in the class com.example.MyVisitor.  The class com.example.MyVisitor must resist in the classpath specified in the taskdef (e.g. another/class/path).

The class specified must implement the interface org.dyndns.fichtner.rsccheck.engine.Visitor (e.g. extend the abstract base class org.dyndns.fichtner.rsccheck.engine.AbstractRscBundleVisitor).

package com.example;

import org.dyndns.fichtner.rsccheck.engine.AbstractRscBundleVisitor;
import org.dyndns.fichtner.rsccheck.engine.RscBundleContent;
import org.dyndns.fichtner.rsccheck.engine.RscBundleReader;
import org.dyndns.fichtner.rsccheck.engine.RscBundleContent.Entry;

public class MyVisitor extends AbstractRscBundleVisitor {

    public String getName() {
        return "foobar check";
    }

    @Override
    public boolean visitBundleKeyValue(RscBundleReader resourcebundle, RscBundleContent content, String rscKey, Entry rscEntry) {
        boolean result = super.visitBundleKeyValue(resourcebundle, content, rscKey, rscEntry);

        if ("foo".equals(rscKey) || "bar".equals(rscKey)) {
            addError(this, "foo or bar are not allowed!", resourcebundle, rscEntry);

        }
        return result;
    }

}


The example checks all keys if their name is "foo" or "bar" and adds an error if a key with that name exists in any resourcebundle defined in the fileset.