KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > LocalizingKeyFinder


1 /*
2  * $Header: /cvsroot/mvnforum/mvnforum/i18n/tool/LocalizingKeyFinder.java,v 1.10 2006/04/15 03:57:15 minhnn Exp $
3  * $Author: minhnn $
4  * $Revision: 1.10 $
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: Phong Ta Quoc
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.BufferedReader JavaDoc;
90 import java.io.File JavaDoc;
91 import java.io.FileInputStream JavaDoc;
92 import java.io.IOException JavaDoc;
93 import java.io.InputStreamReader JavaDoc;
94 import java.util.Hashtable JavaDoc;
95 import java.util.Vector JavaDoc;
96 import org.apache.tools.ant.BuildException;
97 import org.apache.tools.ant.DirectoryScanner;
98 import org.apache.tools.ant.Project;
99 import org.apache.tools.ant.taskdefs.MatchingTask;
100 import org.apache.tools.ant.types.FileSet;
101 import org.apache.tools.ant.util.FileUtils;
102
103 /**
104  * This a a simple finder which find the key are not in a properties file call
105  * bundle file Just find the keys and show its position( file, line in file ).
106  */

107 public class LocalizingKeyFinder extends MatchingTask {
108     /**
109      * Family name of resource bundle
110      */

111     private String JavaDoc bundle;
112     /**
113      * Starting token to identify keys
114      */

115     private String JavaDoc startToken;
116     /**
117      * Ending token to identify keys
118      */

119     private String JavaDoc endToken;
120     /**
121      * Resource Bundle file encoding scheme, defaults to srcEncoding
122      */

123     private String JavaDoc bundleEncoding;
124     /**
125      * Vector to hold source file sets.
126      */

127     private Vector JavaDoc filesets = new Vector JavaDoc();
128     /**
129      * Holds key value pairs loaded from resource bundle file
130      */

131     private Hashtable JavaDoc resourceMap = new Hashtable JavaDoc();
132     /**
133      * Used to resolve file names.
134      */

135     private FileUtils fileUtils = FileUtils.newFileUtils();
136     /**
137      * Has at least one file from the bundle been loaded?
138      */

139     private boolean loaded = false;
140     /**
141      * Sets Family name of resource bundle; required.
142      */

143     public void setBundle(String JavaDoc bundle) {
144         this.bundle = bundle;
145     }
146     /**
147      * Sets starting token to identify keys; required.
148      */

149     public void setStartToken(String JavaDoc startToken) {
150         this.startToken = startToken;
151     }
152     /**
153      * Sets ending token to identify keys; required.
154      */

155     public void setEndToken(String JavaDoc endToken) {
156         this.endToken = endToken;
157     }
158     /**
159      * Sets Resource Bundle file encoding scheme; optional. Defaults to source file
160      * encoding
161      */

162     public void setBundleEncoding(String JavaDoc bundleEncoding) {
163         this.bundleEncoding = bundleEncoding;
164     }
165     /**
166      * Adds a set of files to translate as a nested fileset element.
167      */

168     public void addFileset(FileSet set) {
169         filesets.addElement(set);
170     }
171     /**
172      * Check attributes values, load resource map and translate
173      */

174     public void execute() throws BuildException {
175         if (bundle == null) {
176             throw new BuildException("The bundle attribute must be set.",
177             getLocation());
178         }
179         if (startToken == null) {
180             throw new BuildException("The starttoken attribute must be set.",
181             getLocation());
182         }
183         if (endToken == null) {
184             throw new BuildException("The endtoken attribute must be set.",
185             getLocation());
186         }
187         if (bundleEncoding == null) {
188             bundleEncoding = "Cp1252";
189         }
190
191         loadResourceMaps();
192         translate();
193     }
194     /**
195      * Load resource maps based on resource bundle encoding scheme.
196      * The resource bundle lookup searches for resource files with various
197      * suffixes on the basis of (1) the desired locale and (2) the default
198      * locale (basebundlename), in the following order from lower-level
199      * (more specific) to parent-level (less specific):
200      *
201      * basebundlename + "_" + language1 + "_" + country1 + "_" + variant1
202      * basebundlename + "_" + language1 + "_" + country1
203      * basebundlename + "_" + language1
204      * basebundlename
205      * basebundlename + "_" + language2 + "_" + country2 + "_" + variant2
206      * basebundlename + "_" + language2 + "_" + country2
207      * basebundlename + "_" + language2
208      *
209      * To the generated name, a ".properties" string is appeneded and
210      * once this file is located, it is treated just like a properties file
211      * but with bundle encoding also considered while loading.
212      */

213     private void loadResourceMaps() throws BuildException {
214         processBundle(bundle, true);
215     }
216     /**
217      * Process each file that makes up this bundle.
218      */

219     private void processBundle(final String JavaDoc bundleFile,
220         final boolean checkLoaded) throws BuildException {
221         final File JavaDoc propsFile = new File JavaDoc(bundleFile + ".properties");
222         log(propsFile + "[ BUNDLE PROPERTIES FILE ]");
223         FileInputStream JavaDoc ins = null;
224         try {
225             ins = new FileInputStream JavaDoc(propsFile);
226             loaded = true;
227             log("Using " + propsFile, Project.MSG_DEBUG);
228             loadResourceMap(ins);
229         } catch (IOException JavaDoc ioe) {
230             log(propsFile + " not found.", Project.MSG_DEBUG);
231             if (!loaded && checkLoaded) {
232                 throw new BuildException(ioe.getMessage());
233             }
234         }
235     }
236     /**
237      * Load resourceMap with key value pairs. Values of existing keys are not
238      * overwritten. Bundle's encoding scheme is used.
239      */

240     private void loadResourceMap(FileInputStream JavaDoc ins) throws BuildException {
241         try {
242             BufferedReader JavaDoc in = null;
243             InputStreamReader JavaDoc isr = new InputStreamReader JavaDoc(ins, bundleEncoding);
244             in = new BufferedReader JavaDoc(isr);
245             String JavaDoc line = null;
246             while ((line = in.readLine()) != null) {
247                 //So long as the line isn't empty and isn't a comment...
248
if (line.trim().length() > 1
249                 && ('#' != line.charAt(0) || '!' != line.charAt(0))) {
250                     //Legal Key-Value separators are :, = and white space.
251
int sepIndex = line.indexOf('=');
252                     if (-1 == sepIndex) {
253                         sepIndex = line.indexOf(':');
254                     }
255                     if (-1 == sepIndex) {
256                         for (int k = 0; k < line.length(); k++) {
257                             if (Character.isSpaceChar(line.charAt(k))) {
258                                 sepIndex = k;
259                                 break;
260                             }
261                         }
262                     }
263                     //Only if we do have a key is there going to be a value
264
if (-1 != sepIndex) {
265                         String JavaDoc key = line.substring(0, sepIndex).trim();
266                         String JavaDoc value = line.substring(sepIndex + 1).trim();
267                         //Handle line continuations, if any
268
while (value.endsWith("\\")) {
269                             value = value.substring(0, value.length() - 1);
270                             if ((line = in.readLine()) != null) {
271                                 value = value + line.trim();
272                             } else {
273                                 break;
274                             }
275                         }
276                         if (key.length() > 0) {
277                             //Has key already been loaded into resourceMap?
278
if (resourceMap.get(key) == null) {
279                                 resourceMap.put(key, value);
280                             }
281                         }
282                     }
283                 }
284             }
285             if (in != null) {
286                 in.close();
287             }
288         } catch (IOException JavaDoc ioe) {
289             throw new BuildException(ioe.getMessage());
290         }
291     }
292     /**
293      * Reads source file line by line using the source encoding and searches for
294      * keys that are sandwiched between the startToken and endToken. The values
295      * for these keys are looked up from the hashtable and substituted. If the
296      * hashtable doesn't contain the key, they key itself is used as the value.
297      * Detination files and directories are created as needed. The destination
298      * file is overwritten only if the forceoverwritten attribute is set to true
299      * if the source file or any associated bundle resource file is newer than
300      * the destination file.
301      */

302     private void translate() throws BuildException {
303         for (int i = 0; i < filesets.size(); i++) {
304             FileSet fs = (FileSet) filesets.elementAt(i);
305             DirectoryScanner ds = fs.getDirectoryScanner(getProject());
306             String JavaDoc[] srcFiles = ds.getIncludedFiles();
307             int srcFilesCount = srcFiles.length;
308             if (srcFilesCount == 1) {
309                 log("Checking 1 file in " + fs.getDir(getProject()), Project.MSG_INFO );
310             } else {
311                 log("Checking " + srcFilesCount + " files in " + fs.getDir(getProject()), Project.MSG_INFO);
312             }
313             int bugCount = 0;
314             int warningCount = 0;
315             for (int j = 0; j < srcFiles.length; j++) {
316                 try {
317                     File JavaDoc src = fileUtils.resolveFile(ds.getBasedir(),
318                     srcFiles[j]);
319                     log("Processing " + srcFiles[j], Project.MSG_DEBUG);
320                     FileInputStream JavaDoc fis = new FileInputStream JavaDoc(src);
321                     BufferedReader JavaDoc in = new BufferedReader JavaDoc(
322                         new InputStreamReader JavaDoc(fis));
323                     String JavaDoc line;
324                     int lineNumber = 0;
325                     while ((line = in.readLine()) != null) {
326                         lineNumber++;
327                         int startIndex = -1;
328                         int endIndex = -1;
329                         // just one key in a line ( maybe no loop is also OK !!)
330
outer : while (true) {
331                             startIndex = line.indexOf(startToken, endIndex + 1);
332                             if ((startIndex < 0) || (startIndex + 1 >= line.length()) ) {
333                                 break;
334                             }
335                             endIndex = line.indexOf(endToken, startIndex + 1);
336                             if (endIndex < 0) {
337                                 log("\nWARNING: Line contains the start key " +
338                                     "but doesn't have the end key. ", Project.MSG_WARN);
339                                 log("WARNING: At line " + lineNumber + " in file : " + src, Project.MSG_WARN);
340                                 warningCount++;
341                                 break;
342                             }
343                             startIndex = line.indexOf("\"", startIndex);
344                             if (startIndex < 0) {
345                                 // such as "return MVNForumResourceBundle.getString(locale, this.desc);"
346
if (line.indexOf("this.desc") < 0) {
347                                     log("WARNING: \"" + line + "\" does not have starting quote", Project.MSG_WARN);
348                                     warningCount++;
349                                 }
350                                 break;
351                             }
352                             endIndex = line.indexOf("\"", startIndex + 1);
353                             if (endIndex < 0) {
354                                 // such as "return MVNForumResourceBundle.getString(locale, this.desc);"
355
log("WARNING: \"" + line + "\" does not have ending quote", Project.MSG_WARN);
356                                 warningCount++;
357                                 break;
358                             }
359                             String JavaDoc matches = line.substring(startIndex + 1, endIndex);
360                             //log("[ MATCH ]::match = " + matches + " resource
361
// = " + (String) resourceMap.get(matches),
362
// Project.MSG_WARN);
363
/**
364                              * Note: minhnn I change the code to consider a
365                              * valid matches must begin with mvnforum
366                              */

367                             String JavaDoc replace = null;
368                             replace = (String JavaDoc) resourceMap.get(matches);
369                             //log (replace + "[ WHAT IS REPLACE ??]");
370
if (replace == null) {
371                                 bugCount++;
372                                 log("\nERROR: The key \"" + matches + "\" hasn't been defined.", Project.MSG_WARN);
373                                 log("ERROR: At line " + lineNumber + " in file : " + src, Project.MSG_WARN);
374                                 replace = matches;
375                             }
376                             line = line.substring(0, startIndex) + replace
377                             + line.substring(endIndex + 1);
378                             // minhnn: I dont know if the original code has bug
379
// I changed from "+ 1" to "- 1" and it works well
380
endIndex = startIndex + replace.length() - 1;
381                             if (endIndex + 1 >= line.length()) {
382                                 break;
383                             }
384                         }
385                     }
386                     if (in != null) {
387                         in.close();
388                     }
389                 } catch (IOException JavaDoc ioe) {
390                     throw new BuildException(ioe.getMessage(), getLocation());
391                 }
392             }
393             log("\n###########################################");
394             log("Total error count: " + bugCount);
395             log("Total warning count: " + warningCount);
396         }
397     }
398 }
Popular Tags