Translatepublic class Translate extends org.apache.tools.ant.taskdefs.MatchingTask Translates text embedded in files using Resource Bundle files.
Since ant 1.6 preserves line endings |
Fields Summary |
---|
private static final int | BUNDLE_SPECIFIED_LANGUAGE_COUNTRY_VARIANTsearch a bundle matching the specified language, the country and the variant | private static final int | BUNDLE_SPECIFIED_LANGUAGE_COUNTRYsearch a bundle matching the specified language, and the country | private static final int | BUNDLE_SPECIFIED_LANGUAGEsearch a bundle matching the specified language only | private static final int | BUNDLE_NOMATCHsearch a bundle matching nothing special | private static final int | BUNDLE_DEFAULT_LANGUAGE_COUNTRY_VARIANTsearch a bundle matching the language, the country and the variant
of the current locale of the computer | private static final int | BUNDLE_DEFAULT_LANGUAGE_COUNTRYsearch a bundle matching the language, and the country
of the current locale of the computer | private static final int | BUNDLE_DEFAULT_LANGUAGEsearch a bundle matching the language only
of the current locale of the computer | private static final int | BUNDLE_MAX_ALTERNATIVESnumber of possibilities for the search | private String | bundleFamily name of resource bundle | private String | bundleLanguageLocale specific language of the resource bundle | private String | bundleCountryLocale specific country of the resource bundle | private String | bundleVariantLocale specific variant of the resource bundle | private File | toDirDestination directory | private String | srcEncodingSource file encoding scheme | private String | destEncodingDestination file encoding scheme | private String | bundleEncodingResource Bundle file encoding scheme, defaults to srcEncoding | private String | startTokenStarting token to identify keys | private String | endTokenEnding token to identify keys | private boolean | forceOverwriteWhether or not to create a new destination file.
Defaults to false . | private Vector | filesetsVector to hold source file sets. | private Hashtable | resourceMapHolds key value pairs loaded from resource bundle file | private static final org.apache.tools.ant.util.FileUtils | FILE_UTILSUsed to resolve file names. | private long[] | bundleLastModifiedLast Modified Timestamp of resource bundle file being used. | private long | srcLastModifiedLast Modified Timestamp of source file being used. | private long | destLastModifiedLast Modified Timestamp of destination file being used. | private boolean | loadedHas at least one file from the bundle been loaded? |
Methods Summary |
---|
public void | addFileset(org.apache.tools.ant.types.FileSet set)Adds a set of files to translate as a nested fileset element.
filesets.addElement(set);
| public void | execute()Check attributes values, load resource map and translate
if (bundle == null) {
throw new BuildException("The bundle attribute must be set.",
getLocation());
}
if (startToken == null) {
throw new BuildException("The starttoken attribute must be set.",
getLocation());
}
if (endToken == null) {
throw new BuildException("The endtoken attribute must be set.",
getLocation());
}
if (bundleLanguage == null) {
Locale l = Locale.getDefault();
bundleLanguage = l.getLanguage();
}
if (bundleCountry == null) {
bundleCountry = Locale.getDefault().getCountry();
}
if (bundleVariant == null) {
Locale l = new Locale(bundleLanguage, bundleCountry);
bundleVariant = l.getVariant();
}
if (toDir == null) {
throw new BuildException("The todir attribute must be set.",
getLocation());
}
if (!toDir.exists()) {
toDir.mkdirs();
} else if (toDir.isFile()) {
throw new BuildException(toDir + " is not a directory");
}
if (srcEncoding == null) {
srcEncoding = System.getProperty("file.encoding");
}
if (destEncoding == null) {
destEncoding = srcEncoding;
}
if (bundleEncoding == null) {
bundleEncoding = srcEncoding;
}
loadResourceMaps();
translate();
| private void | loadResourceMap(java.io.FileInputStream ins)Load resourceMap with key value pairs. Values of existing keys
are not overwritten. Bundle's encoding scheme is used.
try {
BufferedReader in = null;
InputStreamReader isr = new InputStreamReader(ins, bundleEncoding);
in = new BufferedReader(isr);
String line = null;
while ((line = in.readLine()) != null) {
//So long as the line isn't empty and isn't a comment...
if (line.trim().length() > 1 && '#" != line.charAt(0) && '!" != line.charAt(0)) {
//Legal Key-Value separators are :, = and white space.
int sepIndex = line.indexOf('=");
if (-1 == sepIndex) {
sepIndex = line.indexOf(':");
}
if (-1 == sepIndex) {
for (int k = 0; k < line.length(); k++) {
if (Character.isSpaceChar(line.charAt(k))) {
sepIndex = k;
break;
}
}
}
//Only if we do have a key is there going to be a value
if (-1 != sepIndex) {
String key = line.substring(0, sepIndex).trim();
String value = line.substring(sepIndex + 1).trim();
//Handle line continuations, if any
while (value.endsWith("\\")) {
value = value.substring(0, value.length() - 1);
if ((line = in.readLine()) != null) {
value = value + line.trim();
} else {
break;
}
}
if (key.length() > 0) {
//Has key already been loaded into resourceMap?
if (resourceMap.get(key) == null) {
resourceMap.put(key, value);
}
}
}
}
}
if (in != null) {
in.close();
}
} catch (IOException ioe) {
throw new BuildException(ioe.getMessage(), getLocation());
}
| private void | loadResourceMaps()Load resource maps based on resource bundle encoding scheme.
The resource bundle lookup searches for resource files with various
suffixes on the basis of (1) the desired locale and (2) the default
locale (basebundlename), in the following order from lower-level
(more specific) to parent-level (less specific):
basebundlename + "_" + language1 + "_" + country1 + "_" + variant1
basebundlename + "_" + language1 + "_" + country1
basebundlename + "_" + language1
basebundlename
basebundlename + "_" + language2 + "_" + country2 + "_" + variant2
basebundlename + "_" + language2 + "_" + country2
basebundlename + "_" + language2
To the generated name, a ".properties" string is appeneded and
once this file is located, it is treated just like a properties file
but with bundle encoding also considered while loading.
Locale locale = new Locale(bundleLanguage,
bundleCountry,
bundleVariant);
String language = locale.getLanguage().length() > 0
? "_" + locale.getLanguage() : "";
String country = locale.getCountry().length() > 0
? "_" + locale.getCountry() : "";
String variant = locale.getVariant().length() > 0
? "_" + locale.getVariant() : "";
String bundleFile = bundle + language + country + variant;
processBundle(bundleFile, BUNDLE_SPECIFIED_LANGUAGE_COUNTRY_VARIANT, false);
bundleFile = bundle + language + country;
processBundle(bundleFile, BUNDLE_SPECIFIED_LANGUAGE_COUNTRY, false);
bundleFile = bundle + language;
processBundle(bundleFile, BUNDLE_SPECIFIED_LANGUAGE, false);
bundleFile = bundle;
processBundle(bundleFile, BUNDLE_NOMATCH, false);
//Load default locale bundle files
//using default file encoding scheme.
locale = Locale.getDefault();
language = locale.getLanguage().length() > 0
? "_" + locale.getLanguage() : "";
country = locale.getCountry().length() > 0
? "_" + locale.getCountry() : "";
variant = locale.getVariant().length() > 0
? "_" + locale.getVariant() : "";
bundleEncoding = System.getProperty("file.encoding");
bundleFile = bundle + language + country + variant;
processBundle(bundleFile, BUNDLE_DEFAULT_LANGUAGE_COUNTRY_VARIANT, false);
bundleFile = bundle + language + country;
processBundle(bundleFile, BUNDLE_DEFAULT_LANGUAGE_COUNTRY, false);
bundleFile = bundle + language;
processBundle(bundleFile, BUNDLE_DEFAULT_LANGUAGE, true);
| private void | processBundle(java.lang.String bundleFile, int i, boolean checkLoaded)Process each file that makes up this bundle.
final File propsFile = getProject().resolveFile(bundleFile + ".properties");
FileInputStream ins = null;
try {
ins = new FileInputStream(propsFile);
loaded = true;
bundleLastModified[i] = propsFile.lastModified();
log("Using " + propsFile, Project.MSG_DEBUG);
loadResourceMap(ins);
} catch (IOException ioe) {
log(propsFile + " not found.", Project.MSG_DEBUG);
//if all resource files associated with this bundle
//have been scanned for and still not able to
//find a single resrouce file, throw exception
if (!loaded && checkLoaded) {
throw new BuildException(ioe.getMessage(), getLocation());
}
}
| public void | setBundle(java.lang.String bundle)Sets Family name of resource bundle; required.
this.bundle = bundle;
| public void | setBundleCountry(java.lang.String bundleCountry)Sets locale specific country of resource bundle; optional.
this.bundleCountry = bundleCountry;
| public void | setBundleEncoding(java.lang.String bundleEncoding)Sets Resource Bundle file encoding scheme; optional. Defaults to source file
encoding
this.bundleEncoding = bundleEncoding;
| public void | setBundleLanguage(java.lang.String bundleLanguage)Sets locale specific language of resource bundle; optional.
this.bundleLanguage = bundleLanguage;
| public void | setBundleVariant(java.lang.String bundleVariant)Sets locale specific variant of resource bundle; optional.
this.bundleVariant = bundleVariant;
| public void | setDestEncoding(java.lang.String destEncoding)Sets destination file encoding scheme; optional. Defaults to source file
encoding
this.destEncoding = destEncoding;
| public void | setEndToken(java.lang.String endToken)Sets ending token to identify keys; required.
this.endToken = endToken;
| public void | setForceOverwrite(boolean forceOverwrite)Whether or not to overwrite existing file irrespective of
whether it is newer than the source file as well as the
resource bundle file.
Defaults to false.
this.forceOverwrite = forceOverwrite;
| public void | setSrcEncoding(java.lang.String srcEncoding)Sets source file encoding scheme; optional,
defaults to encoding of local system.
this.srcEncoding = srcEncoding;
| public void | setStartToken(java.lang.String startToken)Sets starting token to identify keys; required.
this.startToken = startToken;
| public void | setToDir(java.io.File toDir)Sets Destination directory; required.
this.toDir = toDir;
| private void | translate()Reads source file line by line using the source encoding and
searches for keys that are sandwiched between the startToken
and endToken. The values for these keys are looked up from
the hashtable and substituted. If the hashtable doesn't
contain the key, they key itself is used as the value.
Detination files and directories are created as needed.
The destination file is overwritten only if
the forceoverwritten attribute is set to true if
the source file or any associated bundle resource file is
newer than the destination file.
int filesProcessed = 0;
for (int i = 0; i < filesets.size(); i++) {
FileSet fs = (FileSet) filesets.elementAt(i);
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
String[] srcFiles = ds.getIncludedFiles();
for (int j = 0; j < srcFiles.length; j++) {
try {
File dest = FILE_UTILS.resolveFile(toDir, srcFiles[j]);
//Make sure parent dirs exist, else, create them.
try {
File destDir = new File(dest.getParent());
if (!destDir.exists()) {
destDir.mkdirs();
}
} catch (Exception e) {
log("Exception occurred while trying to check/create "
+ " parent directory. " + e.getMessage(),
Project.MSG_DEBUG);
}
destLastModified = dest.lastModified();
File src = FILE_UTILS.resolveFile(ds.getBasedir(), srcFiles[j]);
srcLastModified = src.lastModified();
//Check to see if dest file has to be recreated
boolean needsWork = forceOverwrite
|| destLastModified < srcLastModified;
if (!needsWork) {
for (int icounter = 0; icounter < BUNDLE_MAX_ALTERNATIVES; icounter++) {
needsWork = (destLastModified < bundleLastModified[icounter]);
if (needsWork) {
break;
}
}
}
if (needsWork) {
log("Processing " + srcFiles[j],
Project.MSG_DEBUG);
FileOutputStream fos = new FileOutputStream(dest);
BufferedWriter out
= new BufferedWriter(new OutputStreamWriter(fos, destEncoding));
FileInputStream fis = new FileInputStream(src);
BufferedReader in
= new BufferedReader(new InputStreamReader(fis, srcEncoding));
String line;
LineTokenizer lineTokenizer = new LineTokenizer();
lineTokenizer.setIncludeDelims(true);
line = lineTokenizer.getToken(in);
while ((line) != null) {
// 2003-02-21 new replace algorithm by tbee (tbee@tbee.org)
// because it wasn't able to replace something like "@aaa;@bbb;"
// is there a startToken
// and there is still stuff following the startToken
int startIndex = line.indexOf(startToken);
while (startIndex >= 0
&& (startIndex + startToken.length()) <= line.length()) {
// the new value, this needs to be here
// because it is required to calculate the next position to
// search from at the end of the loop
String replace = null;
// we found a starttoken, is there an endtoken following?
// start at token+tokenlength because start and end
// token may be indentical
int endIndex = line.indexOf(
endToken, startIndex + startToken.length());
if (endIndex < 0) {
startIndex += 1;
} else {
// grab the token
String token = line.substring(
startIndex + startToken.length(), endIndex);
// If there is a white space or = or :, then
// it isn't to be treated as a valid key.
boolean validToken = true;
for (int k = 0; k < token.length() && validToken; k++) {
char c = token.charAt(k);
if (c == ':" || c == '="
|| Character.isSpaceChar(c)) {
validToken = false;
}
}
if (!validToken) {
startIndex += 1;
} else {
// find the replace string
if (resourceMap.containsKey(token)) {
replace = (String) resourceMap.get(token);
} else {
log("Replacement string missing for: "
+ token, Project.MSG_VERBOSE);
replace = startToken + token + endToken;
}
// generate the new line
line = line.substring(0, startIndex)
+ replace
+ line.substring(endIndex + endToken.length());
// set start position for next search
startIndex += replace.length();
}
}
// find next starttoken
startIndex = line.indexOf(startToken, startIndex);
}
out.write(line);
line = lineTokenizer.getToken(in);
}
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
++filesProcessed;
} else {
log("Skipping " + srcFiles[j]
+ " as destination file is up to date",
Project.MSG_VERBOSE);
}
} catch (IOException ioe) {
throw new BuildException(ioe.getMessage(), getLocation());
}
}
}
log("Translation performed on " + filesProcessed + " file(s).", Project.MSG_DEBUG);
|
|