KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > TranslateToJSTL


1 /*
2  * $Header: /cvsroot/mvnforum/mvnforum/i18n/tool/TranslateToJSTL.java,v 1.4 2006/04/15 03:57:15 minhnn Exp $
3  * $Author: minhnn $
4  * $Revision: 1.4 $
5  * $Date: 2006/04/15 03:57:15 $
6  *
7  * ====================================================================
8  *
9  * Copyright (C) 2002-2006 by MyVietnam.net
10  *
11  * All copyright notices regarding MyVietnam and MyVietnam CoreLib
12  * MUST remain intact in the scripts and source code.
13  *
14  * This library is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Lesser General Public
16  * License as published by the Free Software Foundation; either
17  * version 2.1 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22  * Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public
25  * License along with this library; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27  *
28  * Correspondence and Marketing Questions can be sent to:
29  * info at MyVietnam net
30  *
31  * @author: Minh Nguyen
32  * @author: Mai Nguyen
33  */

34 /*
35  * The Apache Software License, Version 1.1
36  *
37  * Copyright (c) 2001-2002 The Apache Software Foundation. All rights
38  * reserved.
39  *
40  * Redistribution and use in source and binary forms, with or without
41  * modification, are permitted provided that the following conditions
42  * are met:
43  *
44  * 1. Redistributions of source code must retain the above copyright
45  * notice, this list of conditions and the following disclaimer.
46  *
47  * 2. Redistributions in binary form must reproduce the above copyright
48  * notice, this list of conditions and the following disclaimer in
49  * the documentation and/or other materials provided with the
50  * distribution.
51  *
52  * 3. The end-user documentation included with the redistribution, if
53  * any, must include the following acknowlegement:
54  * "This product includes software developed by the
55  * Apache Software Foundation (http://www.apache.org/)."
56  * Alternately, this acknowlegement may appear in the software itself,
57  * if and wherever such third-party acknowlegements normally appear.
58  *
59  * 4. The names "The Jakarta Project", "Ant", and "Apache Software
60  * Foundation" must not be used to endorse or promote products derived
61  * from this software without prior written permission. For written
62  * permission, please contact apache@apache.org.
63  *
64  * 5. Products derived from this software may not be called "Apache"
65  * nor may "Apache" appear in their names without prior written
66  * permission of the Apache Group.
67  *
68  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
69  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
70  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
71  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
72  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
73  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
74  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
75  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
76  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
77  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
78  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
79  * SUCH DAMAGE.
80  * ====================================================================
81  *
82  * This software consists of voluntary contributions made by many
83  * individuals on behalf of the Apache Software Foundation. For more
84  * information on the Apache Software Foundation, please see
85  * <http://www.apache.org/>.
86  */

87 //package org.apache.tools.ant.taskdefs.optional.i18n;
88

89 import java.io.File JavaDoc;
90 import java.io.FileInputStream JavaDoc;
91 import java.io.IOException JavaDoc;
92 import java.io.BufferedReader JavaDoc;
93 import java.io.InputStreamReader JavaDoc;
94 import java.io.FileOutputStream JavaDoc;
95 import java.io.BufferedWriter JavaDoc;
96 import java.io.OutputStreamWriter JavaDoc;
97 import java.util.Vector JavaDoc;
98 import java.util.Hashtable JavaDoc;
99 import java.util.Locale JavaDoc;
100 import org.apache.tools.ant.BuildException;
101 import org.apache.tools.ant.Project;
102 import org.apache.tools.ant.DirectoryScanner;
103 import org.apache.tools.ant.types.FileSet;
104 import org.apache.tools.ant.util.FileUtils;
105 import org.apache.tools.ant.taskdefs.MatchingTask;
106
107 /**
108  * Translates text embedded in files using Resource Bundle files.
109  *
110  * @author Magesh Umasankar
111  */

112 public class TranslateToJSTL extends MatchingTask {
113
114     /**
115      * Family name of resource bundle
116      */

117     private String JavaDoc bundle;
118     /**
119      * Locale specific language of the resource bundle
120      */

121     private String JavaDoc bundleLanguage;
122     /**
123      * Locale specific country of the resource bundle
124      */

125     private String JavaDoc bundleCountry;
126     /**
127      * Locale specific variant of the resource bundle
128      */

129     private String JavaDoc bundleVariant;
130     /**
131      * Destination directory
132      */

133     private File JavaDoc toDir;
134     /**
135      * Source file encoding scheme
136      */

137     private String JavaDoc srcEncoding;
138     /**
139      * Destination file encoding scheme
140      */

141     private String JavaDoc destEncoding;
142     /**
143      * Resource Bundle file encoding scheme, defaults to srcEncoding
144      */

145     private String JavaDoc bundleEncoding;
146     /**
147      * Starting token to identify keys
148      */

149     private String JavaDoc startToken;
150     /**
151      * Ending token to identify keys
152      */

153     private String JavaDoc endToken;
154     /**
155      * Whether or not to create a new destination file.
156      * Defaults to <code>false</code>.
157      */

158     private boolean forceOverwrite;
159     /**
160      * Vector to hold source file sets.
161      */

162     private Vector JavaDoc filesets = new Vector JavaDoc();
163     /**
164      * Holds key value pairs loaded from resource bundle file
165      */

166     private Hashtable JavaDoc resourceMap = new Hashtable JavaDoc();
167     /**
168      * Generated locale based on user attributes
169      */

170     private Locale JavaDoc locale;
171     /**
172      * Used to resolve file names.
173      */

174     private FileUtils fileUtils = FileUtils.newFileUtils();
175     /**
176      * Last Modified Timestamp of resource bundle file being used.
177      */

178     private long[] bundleLastModified = new long[7];
179     /**
180      * Last Modified Timestamp of source file being used.
181      */

182     private long srcLastModified;
183     /**
184      * Last Modified Timestamp of destination file being used.
185      */

186     private long destLastModified;
187     /**
188      * Has at least one file from the bundle been loaded?
189      */

190     private boolean loaded = false;
191
192     /**
193      * Sets Family name of resource bundle; required.
194      */

195     public void setBundle(String JavaDoc bundle) {
196         this.bundle = bundle;
197     }
198
199     /**
200      * Sets locale specific language of resource bundle; optional.
201      */

202     public void setBundleLanguage(String JavaDoc bundleLanguage) {
203         this.bundleLanguage = bundleLanguage;
204     }
205
206     /**
207      * Sets locale specific country of resource bundle; optional.
208      */

209     public void setBundleCountry(String JavaDoc bundleCountry) {
210         this.bundleCountry = bundleCountry;
211     }
212
213     /**
214      * Sets locale specific variant of resource bundle; optional.
215      */

216     public void setBundleVariant(String JavaDoc bundleVariant) {
217         this.bundleVariant = bundleVariant;
218     }
219
220     /**
221      * Sets Destination directory; required.
222      */

223     public void setToDir(File JavaDoc toDir) {
224         this.toDir = toDir;
225     }
226
227     /**
228      * Sets starting token to identify keys; required.
229      */

230     public void setStartToken(String JavaDoc startToken) {
231         this.startToken = startToken;
232     }
233
234     /**
235      * Sets ending token to identify keys; required.
236      */

237     public void setEndToken(String JavaDoc endToken) {
238         this.endToken = endToken;
239     }
240
241     /**
242      * Sets source file encoding scheme; optional,
243      * defaults to encoding of local system.
244      */

245     public void setSrcEncoding(String JavaDoc srcEncoding) {
246         this.srcEncoding = srcEncoding;
247     }
248
249     /**
250      * Sets destination file encoding scheme; optional. Defaults to source file
251      * encoding
252      */

253     public void setDestEncoding(String JavaDoc destEncoding) {
254         this.destEncoding = destEncoding;
255     }
256
257     /**
258      * Sets Resource Bundle file encoding scheme; optional. Defaults to source file
259      * encoding
260      */

261     public void setBundleEncoding(String JavaDoc bundleEncoding) {
262         this.bundleEncoding = bundleEncoding;
263     }
264
265     /**
266      * Whether or not to overwrite existing file irrespective of
267      * whether it is newer than the source file as well as the
268      * resource bundle file.
269      * Defaults to false.
270      */

271     public void setForceOverwrite(boolean forceOverwrite) {
272         this.forceOverwrite = forceOverwrite;
273     }
274
275     /**
276      * Adds a set of files to translate as a nested fileset element.
277      */

278     public void addFileset(FileSet set) {
279         filesets.addElement(set);
280     }
281
282     /**
283      * Check attributes values, load resource map and translate
284      */

285     public void execute() throws BuildException {
286         if (bundle == null) {
287             throw new BuildException("The bundle attribute must be set.",
288                                     getLocation());
289                                      
290         }
291
292         if (startToken == null) {
293             throw new BuildException("The starttoken attribute must be set.",
294                                     getLocation());
295         }
296
297         if (startToken.length() != 1) {
298             throw new BuildException(
299                 "The starttoken attribute must be a single character.",
300                                     getLocation());
301         }
302                     
303         if (endToken == null) {
304             throw new BuildException("The endtoken attribute must be set.",
305                                      getLocation());
306         }
307
308         if (endToken.length() != 1) {
309             throw new BuildException(
310                 "The endtoken attribute must be a single character.",
311                                          getLocation());
312         }
313
314         if (bundleLanguage == null) {
315             Locale JavaDoc l = Locale.getDefault();
316             bundleLanguage = l.getLanguage();
317         }
318
319         if (bundleCountry == null) {
320             bundleCountry = Locale.getDefault().getCountry();
321         }
322
323         locale = new Locale JavaDoc(bundleLanguage, bundleCountry);
324
325         if (bundleVariant == null) {
326             Locale JavaDoc l = new Locale JavaDoc(bundleLanguage, bundleCountry);
327             bundleVariant = l.getVariant();
328         }
329
330         if (toDir == null) {
331             throw new BuildException("The todir attribute must be set.",
332                                      getLocation());
333         }
334
335         if (!toDir.exists()) {
336             toDir.mkdirs();
337         } else {
338             if (toDir.isFile()) {
339                 throw new BuildException(toDir + " is not a directory");
340             }
341         }
342
343         if (srcEncoding == null) {
344             srcEncoding = System.getProperty("file.encoding");
345         }
346
347         if (destEncoding == null) {
348             destEncoding = srcEncoding;
349         }
350
351         if (bundleEncoding == null) {
352             bundleEncoding = srcEncoding;
353         }
354
355         loadResourceMaps();
356
357         translate();
358     }
359
360     /**
361      * Load resource maps based on resource bundle encoding scheme.
362      * The resource bundle lookup searches for resource files with various
363      * suffixes on the basis of (1) the desired locale and (2) the default
364      * locale (basebundlename), in the following order from lower-level
365      * (more specific) to parent-level (less specific):
366      *
367      * basebundlename + "_" + language1 + "_" + country1 + "_" + variant1
368      * basebundlename + "_" + language1 + "_" + country1
369      * basebundlename + "_" + language1
370      * basebundlename
371      * basebundlename + "_" + language2 + "_" + country2 + "_" + variant2
372      * basebundlename + "_" + language2 + "_" + country2
373      * basebundlename + "_" + language2
374      *
375      * To the generated name, a ".properties" string is appeneded and
376      * once this file is located, it is treated just like a properties file
377      * but with bundle encoding also considered while loading.
378      */

379     private void loadResourceMaps() throws BuildException {
380         Locale JavaDoc locale = new Locale JavaDoc(bundleLanguage,
381                                    bundleCountry,
382                                    bundleVariant);
383         String JavaDoc language = locale.getLanguage().length() > 0 ?
384             "_" + locale.getLanguage() :
385             "";
386         String JavaDoc country = locale.getCountry().length() > 0 ?
387             "_" + locale.getCountry() :
388             "";
389         String JavaDoc variant = locale.getVariant().length() > 0 ?
390             "_" + locale.getVariant() :
391             "";
392         String JavaDoc bundleFile = bundle + language + country + variant;
393         processBundle(bundleFile, 0, false);
394
395         bundleFile = bundle + language + country;
396         processBundle(bundleFile, 1, false);
397
398         bundleFile = bundle + language;
399         processBundle(bundleFile, 2, false);
400
401         bundleFile = bundle;
402         processBundle(bundleFile, 3, false);
403
404         //Load default locale bundle files
405
//using default file encoding scheme.
406
locale = Locale.getDefault();
407
408         language = locale.getLanguage().length() > 0 ?
409             "_" + locale.getLanguage() :
410             "";
411         country = locale.getCountry().length() > 0 ?
412             "_" + locale.getCountry() :
413             "";
414         variant = locale.getVariant().length() > 0 ?
415             "_" + locale.getVariant() :
416             "";
417         bundleEncoding = System.getProperty("file.encoding");
418
419         bundleFile = bundle + language + country + variant;
420         processBundle(bundleFile, 4, false);
421
422         bundleFile = bundle + language + country;
423         processBundle(bundleFile, 5, false);
424
425         bundleFile = bundle + language;
426         processBundle(bundleFile, 6, true);
427     }
428
429     /**
430      * Process each file that makes up this bundle.
431      */

432     private void processBundle(final String JavaDoc bundleFile, final int i,
433                                final boolean checkLoaded) throws BuildException {
434         final File JavaDoc propsFile = new File JavaDoc(bundleFile + ".properties");
435         FileInputStream JavaDoc ins = null;
436         try {
437             ins = new FileInputStream JavaDoc(propsFile);
438             loaded = true;
439             bundleLastModified[i] = propsFile.lastModified();
440             log("Using " + propsFile, Project.MSG_DEBUG);
441             loadResourceMap(ins);
442         } catch (IOException JavaDoc ioe) {
443             log(propsFile + " not found.", Project.MSG_DEBUG);
444             //if all resource files associated with this bundle
445
//have been scanned for and still not able to
446
//find a single resrouce file, throw exception
447
if (!loaded && checkLoaded) {
448                 throw new BuildException(ioe.getMessage(), getLocation());
449             }
450         }
451     }
452
453     /**
454      * Load resourceMap with key value pairs. Values of existing keys
455      * are not overwritten. Bundle's encoding scheme is used.
456      */

457     private void loadResourceMap(FileInputStream JavaDoc ins) throws BuildException {
458         try {
459             BufferedReader JavaDoc in = null;
460             InputStreamReader JavaDoc isr = new InputStreamReader JavaDoc(ins, bundleEncoding);
461             in = new BufferedReader JavaDoc(isr);
462             String JavaDoc line = null;
463             while ((line = in.readLine()) != null) {
464                 //So long as the line isn't empty and isn't a comment...
465
if (line.trim().length() > 1 &&
466                    ('#' != line.charAt(0) || '!' != line.charAt(0))) {
467                     //Legal Key-Value separators are :, = and white space.
468
int sepIndex = line.indexOf('=');
469                     if (-1 == sepIndex) {
470                         sepIndex = line.indexOf(':');
471                     }
472                     if (-1 == sepIndex) {
473                         for (int k = 0; k < line.length(); k++) {
474                             if (Character.isSpaceChar(line.charAt(k))) {
475                                 sepIndex = k;
476                                 break;
477                             }
478                         }
479                     }
480                     //Only if we do have a key is there going to be a value
481
if (-1 != sepIndex) {
482                         String JavaDoc key = line.substring(0, sepIndex).trim();
483                         String JavaDoc value = line.substring(sepIndex + 1).trim();
484                         //Handle line continuations, if any
485
while (value.endsWith("\\")) {
486                             value = value.substring(0, value.length() - 1);
487                             if ((line = in.readLine()) != null) {
488                                 value = value + line.trim();
489                             } else {
490                                 break;
491                             }
492                         }
493                         if (key.length() > 0) {
494                             //Has key already been loaded into resourceMap?
495
if (resourceMap.get(key) == null) {
496                                 resourceMap.put(key, value);
497                             }
498                         }
499                     }
500                 }
501             }
502             if (in != null) {
503                 in.close();
504             }
505         } catch (IOException JavaDoc ioe) {
506             throw new BuildException(ioe.getMessage(), getLocation());
507         }
508     }
509
510     /**
511      * Reads source file line by line using the source encoding and
512      * searches for keys that are sandwiched between the startToken
513      * and endToken. The values for these keys are looked up from
514      * the hashtable and substituted. If the hashtable doesn't
515      * contain the key, they key itself is used as the value.
516      * Detination files and directories are created as needed.
517      * The destination file is overwritten only if
518      * the forceoverwritten attribute is set to true if
519      * the source file or any associated bundle resource file is
520      * newer than the destination file.
521      */

522     private void translate() throws BuildException {
523         for (int i = 0; i < filesets.size(); i++) {
524             FileSet fs = (FileSet) filesets.elementAt(i);
525             DirectoryScanner ds = fs.getDirectoryScanner(getProject());
526             String JavaDoc[] srcFiles = ds.getIncludedFiles();
527             int srcFilesCount = srcFiles.length;
528             if (srcFilesCount == 1) {
529                 log("Translating 1 file", Project.MSG_INFO);
530             } else {
531                 log("Translating " + srcFilesCount + " files", Project.MSG_INFO);
532             }
533             for (int j = 0; j < srcFiles.length; j++) {
534                 try {
535                     File JavaDoc dest = fileUtils.resolveFile(toDir, srcFiles[j]);
536                     //Make sure parent dirs exist, else, create them.
537
try {
538                         File JavaDoc destDir = new File JavaDoc(dest.getParent());
539                         if (!destDir.exists()) {
540                             destDir.mkdirs();
541                         }
542                     } catch (Exception JavaDoc e) {
543                         log("Exception occured while trying to check/create "
544                             + " parent directory. " + e.getMessage(),
545                             Project.MSG_DEBUG);
546                     }
547                     destLastModified = dest.lastModified();
548                     File JavaDoc src = fileUtils.resolveFile(ds.getBasedir(), srcFiles[j]);
549                     srcLastModified = src.lastModified();
550                     //Check to see if dest file has to be recreated
551
if (forceOverwrite
552                         || destLastModified < srcLastModified
553                         || destLastModified < bundleLastModified[0]
554                         || destLastModified < bundleLastModified[1]
555                         || destLastModified < bundleLastModified[2]
556                         || destLastModified < bundleLastModified[3]
557                         || destLastModified < bundleLastModified[4]
558                         || destLastModified < bundleLastModified[5]
559                         || destLastModified < bundleLastModified[6]) {
560                         log("Processing " + srcFiles[j],
561                             Project.MSG_DEBUG);
562                         FileOutputStream JavaDoc fos = new FileOutputStream JavaDoc(dest);
563                         BufferedWriter JavaDoc out
564                             = new BufferedWriter JavaDoc(new OutputStreamWriter JavaDoc(fos, destEncoding));
565                         FileInputStream JavaDoc fis = new FileInputStream JavaDoc(src);
566                         BufferedReader JavaDoc in
567                             = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(fis, srcEncoding));
568                         String JavaDoc line;
569                         while ((line = in.readLine()) != null) {
570                             int startIndex = -1;
571                             int endIndex = -1;
572 outer: while (true) {
573                                 startIndex = line.indexOf(startToken, endIndex + 1);
574                                 if (startIndex < 0 ||
575                                     startIndex + 1 >= line.length()) {
576                                     break;
577                                 }
578                                 endIndex = line.indexOf(endToken, startIndex + 1);
579                                 if (endIndex < 0) {
580                                     break;
581                                 }
582                                 String JavaDoc matches = line.substring(startIndex + 1,
583                                                                 endIndex);
584                                 //log("match = " + matches + " resource = " + (String) resourceMap.get(matches), Project.MSG_WARN);
585
/**
586                                  * Note: minhnn
587                                  * I change the code to consider a valid
588                                  * matches must begin with mvnforum
589                                  */

590                                 if (matches.startsWith("mvnforum.") == false ) {
591                                     endIndex = endIndex - 1;
592                                     continue outer;
593                                 }
594                                 //If there is a white space or = or :, then
595
//it isn't to be treated as a valid key.
596
for (int k = 0; k < matches.length(); k++) {
597                                     char c = matches.charAt(k);
598                                     if (c == ':' ||
599                                         c == '=' ||
600                                         Character.isSpaceChar(c)) {
601                                         endIndex = endIndex - 1;
602                                         continue outer;
603                                     }
604                                 }
605                                 String JavaDoc replace = null;
606                                 replace = "<fmt:message key=\"" + matches + "\"/>";//(String) resourceMap.get(matches);
607

608                                 //If the key hasn't been loaded into resourceMap,
609
//use the key itself as the value also.
610
if (replace == null) {
611                                     log("Warning: The key: " + matches
612                                         + " hasn't been defined.",
613                                         Project.MSG_WARN);
614                                     replace = matches;
615                                 }
616                                 line = line.substring(0, startIndex)
617                                     + replace
618                                     + line.substring(endIndex + 1);
619                                 // minhnn: I dont know if the original code has bug
620
// I changed from "+ 1" to "- 1" and it works well
621
endIndex = startIndex + replace.length() - 1;
622                                 if (endIndex + 1 >= line.length()) {
623                                     break;
624                                 }
625                             }
626                             out.write(line);
627                             out.newLine();
628                         }
629                         if (in != null) {
630                             in.close();
631                         }
632                         if (out != null) {
633                             out.close();
634                         }
635                     } else {
636                         log("Skipping " + srcFiles[j] +
637                             " as destination file is up to date",
638                             Project.MSG_VERBOSE);
639                     }
640                 } catch (IOException JavaDoc ioe) {
641                     throw new BuildException(ioe.getMessage(), getLocation());
642                 }
643             }
644         }
645     }
646 }
647
Popular Tags