FileDocCategorySizeDatePackage
Translate.javaAPI DocApache Ant 1.7023940Wed Dec 13 06:16:22 GMT 2006org.apache.tools.ant.taskdefs.optional.i18n

Translate

public 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_VARIANT
search a bundle matching the specified language, the country and the variant
private static final int
BUNDLE_SPECIFIED_LANGUAGE_COUNTRY
search a bundle matching the specified language, and the country
private static final int
BUNDLE_SPECIFIED_LANGUAGE
search a bundle matching the specified language only
private static final int
BUNDLE_NOMATCH
search a bundle matching nothing special
private static final int
BUNDLE_DEFAULT_LANGUAGE_COUNTRY_VARIANT
search a bundle matching the language, the country and the variant of the current locale of the computer
private static final int
BUNDLE_DEFAULT_LANGUAGE_COUNTRY
search a bundle matching the language, and the country of the current locale of the computer
private static final int
BUNDLE_DEFAULT_LANGUAGE
search a bundle matching the language only of the current locale of the computer
private static final int
BUNDLE_MAX_ALTERNATIVES
number of possibilities for the search
private String
bundle
Family name of resource bundle
private String
bundleLanguage
Locale specific language of the resource bundle
private String
bundleCountry
Locale specific country of the resource bundle
private String
bundleVariant
Locale specific variant of the resource bundle
private File
toDir
Destination directory
private String
srcEncoding
Source file encoding scheme
private String
destEncoding
Destination file encoding scheme
private String
bundleEncoding
Resource Bundle file encoding scheme, defaults to srcEncoding
private String
startToken
Starting token to identify keys
private String
endToken
Ending token to identify keys
private boolean
forceOverwrite
Whether or not to create a new destination file. Defaults to false.
private Vector
filesets
Vector to hold source file sets.
private Hashtable
resourceMap
Holds key value pairs loaded from resource bundle file
private static final org.apache.tools.ant.util.FileUtils
FILE_UTILS
Used to resolve file names.
private long[]
bundleLastModified
Last Modified Timestamp of resource bundle file being used.
private long
srcLastModified
Last Modified Timestamp of source file being used.
private long
destLastModified
Last Modified Timestamp of destination file being used.
private boolean
loaded
Has at least one file from the bundle been loaded?
Constructors Summary
Methods Summary
public voidaddFileset(org.apache.tools.ant.types.FileSet set)
Adds a set of files to translate as a nested fileset element.

param
set the fileset to be added

        filesets.addElement(set);
    
public voidexecute()
Check attributes values, load resource map and translate

throws
BuildException if the required attributes are not set Required :
  • bundle
  • starttoken
  • endtoken

        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 voidloadResourceMap(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 voidloadResourceMaps()
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 voidprocessBundle(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 voidsetBundle(java.lang.String bundle)
Sets Family name of resource bundle; required.

param
bundle family name of resource bundle


                       
        
        this.bundle = bundle;
    
public voidsetBundleCountry(java.lang.String bundleCountry)
Sets locale specific country of resource bundle; optional.

param
bundleCountry country of the bundle

        this.bundleCountry = bundleCountry;
    
public voidsetBundleEncoding(java.lang.String bundleEncoding)
Sets Resource Bundle file encoding scheme; optional. Defaults to source file encoding

param
bundleEncoding bundle file encoding scheme

        this.bundleEncoding = bundleEncoding;
    
public voidsetBundleLanguage(java.lang.String bundleLanguage)
Sets locale specific language of resource bundle; optional.

param
bundleLanguage langage of the bundle

        this.bundleLanguage = bundleLanguage;
    
public voidsetBundleVariant(java.lang.String bundleVariant)
Sets locale specific variant of resource bundle; optional.

param
bundleVariant locale variant of resource bundle

        this.bundleVariant = bundleVariant;
    
public voidsetDestEncoding(java.lang.String destEncoding)
Sets destination file encoding scheme; optional. Defaults to source file encoding

param
destEncoding destination file encoding scheme

        this.destEncoding = destEncoding;
    
public voidsetEndToken(java.lang.String endToken)
Sets ending token to identify keys; required.

param
endToken ending token to identify keys

        this.endToken = endToken;
    
public voidsetForceOverwrite(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.

param
forceOverwrite whether or not to overwrite existing files

        this.forceOverwrite = forceOverwrite;
    
public voidsetSrcEncoding(java.lang.String srcEncoding)
Sets source file encoding scheme; optional, defaults to encoding of local system.

param
srcEncoding source file encoding

        this.srcEncoding = srcEncoding;
    
public voidsetStartToken(java.lang.String startToken)
Sets starting token to identify keys; required.

param
startToken starting token to identify keys

        this.startToken = startToken;
    
public voidsetToDir(java.io.File toDir)
Sets Destination directory; required.

param
toDir destination directory

        this.toDir = toDir;
    
private voidtranslate()
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);