aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/i18n/Translate.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/i18n/Translate.java')
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/i18n/Translate.java632
1 files changed, 632 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/i18n/Translate.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/i18n/Translate.java
new file mode 100644
index 00000000..82731fe9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/i18n/Translate.java
@@ -0,0 +1,632 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.i18n;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.LineTokenizer;
+
+/**
+ * Translates text embedded in files using Resource Bundle files.
+ * Since ant 1.6 preserves line endings
+ *
+ */
+public class Translate extends MatchingTask {
+ /**
+ * search a bundle matching the specified language, the country and the variant
+ */
+ private static final int BUNDLE_SPECIFIED_LANGUAGE_COUNTRY_VARIANT = 0;
+ /**
+ * search a bundle matching the specified language, and the country
+ */
+ private static final int BUNDLE_SPECIFIED_LANGUAGE_COUNTRY = 1;
+ /**
+ * search a bundle matching the specified language only
+ */
+ private static final int BUNDLE_SPECIFIED_LANGUAGE = 2;
+ /**
+ * search a bundle matching nothing special
+ */
+ private static final int BUNDLE_NOMATCH = 3;
+ /**
+ * 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_VARIANT = 4;
+ /**
+ * search a bundle matching the language, and the country
+ * of the current locale of the computer
+ */
+ private static final int BUNDLE_DEFAULT_LANGUAGE_COUNTRY = 5;
+ /**
+ * search a bundle matching the language only
+ * of the current locale of the computer
+ */
+ private static final int BUNDLE_DEFAULT_LANGUAGE = 6;
+ /**
+ * number of possibilities for the search
+ */
+ private static final int BUNDLE_MAX_ALTERNATIVES = BUNDLE_DEFAULT_LANGUAGE + 1;
+ /**
+ * Family name of resource bundle
+ */
+ private String bundle;
+
+ /**
+ * Locale specific language of the resource bundle
+ */
+ private String bundleLanguage;
+
+ /**
+ * Locale specific country of the resource bundle
+ */
+ private String bundleCountry;
+
+ /**
+ * Locale specific variant of the resource bundle
+ */
+ private String bundleVariant;
+
+ /**
+ * Destination directory
+ */
+ private File toDir;
+
+ /**
+ * Source file encoding scheme
+ */
+ private String srcEncoding;
+
+ /**
+ * Destination file encoding scheme
+ */
+ private String destEncoding;
+
+ /**
+ * Resource Bundle file encoding scheme, defaults to srcEncoding
+ */
+ private String bundleEncoding;
+
+ /**
+ * Starting token to identify keys
+ */
+ private String startToken;
+
+ /**
+ * Ending token to identify keys
+ */
+ private String endToken;
+
+ /**
+ * Whether or not to create a new destination file.
+ * Defaults to <code>false</code>.
+ */
+ private boolean forceOverwrite;
+
+ /**
+ * Vector to hold source file sets.
+ */
+ private Vector filesets = new Vector();
+
+ /**
+ * Holds key value pairs loaded from resource bundle file
+ */
+ private Hashtable resourceMap = new Hashtable();
+ /**
+
+ * Used to resolve file names.
+ */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Last Modified Timestamp of resource bundle file being used.
+ */
+ private long[] bundleLastModified = new long[BUNDLE_MAX_ALTERNATIVES];
+
+ /**
+ * Last Modified Timestamp of source file being used.
+ */
+ private long srcLastModified;
+
+ /**
+ * Last Modified Timestamp of destination file being used.
+ */
+ private long destLastModified;
+
+ /**
+ * Has at least one file from the bundle been loaded?
+ */
+ private boolean loaded = false;
+
+ /**
+ * Sets Family name of resource bundle; required.
+ * @param bundle family name of resource bundle
+ */
+ public void setBundle(String bundle) {
+ this.bundle = bundle;
+ }
+
+ /**
+ * Sets locale specific language of resource bundle; optional.
+ * @param bundleLanguage language of the bundle
+ */
+ public void setBundleLanguage(String bundleLanguage) {
+ this.bundleLanguage = bundleLanguage;
+ }
+
+ /**
+ * Sets locale specific country of resource bundle; optional.
+ * @param bundleCountry country of the bundle
+ */
+ public void setBundleCountry(String bundleCountry) {
+ this.bundleCountry = bundleCountry;
+ }
+
+ /**
+ * Sets locale specific variant of resource bundle; optional.
+ * @param bundleVariant locale variant of resource bundle
+ */
+ public void setBundleVariant(String bundleVariant) {
+ this.bundleVariant = bundleVariant;
+ }
+
+ /**
+ * Sets Destination directory; required.
+ * @param toDir destination directory
+ */
+ public void setToDir(File toDir) {
+ this.toDir = toDir;
+ }
+
+ /**
+ * Sets starting token to identify keys; required.
+ * @param startToken starting token to identify keys
+ */
+ public void setStartToken(String startToken) {
+ this.startToken = startToken;
+ }
+
+ /**
+ * Sets ending token to identify keys; required.
+ * @param endToken ending token to identify keys
+ */
+ public void setEndToken(String endToken) {
+ this.endToken = endToken;
+ }
+
+ /**
+ * Sets source file encoding scheme; optional,
+ * defaults to encoding of local system.
+ * @param srcEncoding source file encoding
+ */
+ public void setSrcEncoding(String srcEncoding) {
+ this.srcEncoding = srcEncoding;
+ }
+
+ /**
+ * Sets destination file encoding scheme; optional. Defaults to source file
+ * encoding
+ * @param destEncoding destination file encoding scheme
+ */
+ public void setDestEncoding(String destEncoding) {
+ this.destEncoding = destEncoding;
+ }
+
+ /**
+ * Sets Resource Bundle file encoding scheme; optional. Defaults to source file
+ * encoding
+ * @param bundleEncoding bundle file encoding scheme
+ */
+ public void setBundleEncoding(String bundleEncoding) {
+ this.bundleEncoding = bundleEncoding;
+ }
+
+ /**
+ * 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
+ */
+ public void setForceOverwrite(boolean forceOverwrite) {
+ this.forceOverwrite = forceOverwrite;
+ }
+
+ /**
+ * Adds a set of files to translate as a nested fileset element.
+ * @param set the fileset to be added
+ */
+ public void addFileset(FileSet set) {
+ filesets.addElement(set);
+ }
+
+ /**
+ * Check attributes values, load resource map and translate
+ * @throws BuildException if the required attributes are not set
+ * Required : <ul>
+ * <li>bundle</li>
+ * <li>starttoken</li>
+ * <li>endtoken</li>
+ * </ul>
+ */
+ public void execute() throws BuildException {
+ 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();
+ }
+
+ /**
+ * 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.
+ */
+ private void loadResourceMaps() throws BuildException {
+ 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);
+ }
+
+ /**
+ * Process each file that makes up this bundle.
+ */
+ private void processBundle(final String bundleFile, final int i,
+ final boolean checkLoaded) throws BuildException {
+ 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());
+ }
+ }
+ }
+
+ /**
+ * Load resourceMap with key value pairs. Values of existing keys
+ * are not overwritten. Bundle's encoding scheme is used.
+ */
+ private void loadResourceMap(FileInputStream ins) throws BuildException {
+ 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);
+ line = in.readLine();
+ if (line != 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());
+ }
+ }
+
+ /**
+ * 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.
+ */
+ private void translate() throws BuildException {
+ int filesProcessed = 0;
+ final int size = filesets.size();
+ for (int i = 0; i < 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);
+ translateOneFile(src, dest);
+ ++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);
+ }
+
+ private void translateOneFile(File src, File dest) throws IOException {
+ BufferedWriter out = null;
+ BufferedReader in = null;
+ try {
+ FileOutputStream fos = new FileOutputStream(dest);
+ out = new BufferedWriter(new OutputStreamWriter(fos, destEncoding));
+ FileInputStream fis = new FileInputStream(src);
+ 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 identical
+ 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);
+ }
+ } finally {
+ FileUtils.close(in);
+ FileUtils.close(out);
+ }
+ }
+}