KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > tigris > scarab > util > build > l10nchecker > L10nInspector


1 package org.tigris.scarab.util.build.l10nchecker;
2
3 /* ================================================================
4  * Copyright (c) 2005 CollabNet. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in the
15  * documentation and/or other materials provided with the distribution.
16  *
17  * 3. The end-user documentation included with the redistribution, if
18  * any, must include the following acknowlegement: "This product includes
19  * software developed by Collab.Net <http://www.Collab.Net/>."
20  * Alternately, this acknowlegement may appear in the software itself, if
21  * and wherever such third-party acknowlegements normally appear.
22  *
23  * 4. The hosted project names must not be used to endorse or promote
24  * products derived from this software without prior written
25  * permission. For written permission, please contact info@collab.net.
26  *
27  * 5. Products derived from this software may not use the "Tigris" or
28  * "Scarab" names nor may "Tigris" or "Scarab" appear in their names without
29  * prior written permission of Collab.Net.
30  *
31  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
32  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
33  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
34  * IN NO EVENT SHALL COLLAB.NET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
35  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
37  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
39  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
40  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42  *
43  * ====================================================================
44  *
45  * This software consists of voluntary contributions made by many
46  * individuals on behalf of Collab.Net.
47  */

48
49 import java.io.BufferedReader JavaDoc;
50 import java.io.File JavaDoc;
51 import java.io.FileReader JavaDoc;
52 import java.io.IOException JavaDoc;
53 import java.text.MessageFormat JavaDoc;
54 import java.util.ArrayList JavaDoc;
55 import java.util.Hashtable JavaDoc;
56 import java.util.Iterator JavaDoc;
57 import java.util.List JavaDoc;
58
59 import org.apache.oro.text.regex.MalformedPatternException;
60 import org.apache.oro.text.regex.MatchResult;
61 import org.apache.oro.text.regex.Pattern;
62 import org.apache.oro.text.regex.Perl5Compiler;
63 import org.apache.oro.text.regex.Perl5Matcher;
64
65 import org.tigris.scarab.util.Log;
66 import org.tigris.scarab.util.build.l10nchecker.issues.CantParseLineIssue;
67 import org.tigris.scarab.util.build.l10nchecker.issues.DefinedTwiceIssue;
68 import org.tigris.scarab.util.build.l10nchecker.issues.DifferentAttributeCountIssue;
69 import org.tigris.scarab.util.build.l10nchecker.issues.IllegalPatternIssue;
70 import org.tigris.scarab.util.build.l10nchecker.issues.NoTransAllowedIssue;
71 import org.tigris.scarab.util.build.l10nchecker.issues.NotInReferenceIssue;
72 import org.tigris.scarab.util.build.l10nchecker.issues.NotTranslatedIssue;
73 import org.tigris.scarab.util.build.l10nchecker.issues.TranslatedTwiceDiffIssue;
74 import org.tigris.scarab.util.build.l10nchecker.issues.TranslatedTwiceIssue;
75 import org.tigris.scarab.util.build.l10nchecker.issues.TranslationMissingIssue;
76 import org.tigris.scarab.util.build.l10nchecker.issues.TranslationRequiredIssue;
77
78 /**
79  * Scarab language checker.
80  *
81  * <p>
82  * A L10nInspector represents a class that is reading a property file (see
83  * {@link #setReference(String)}) containing l10n properties as a reference and then
84  * running through a given list of other property files to check if all
85  * localisations that are needed are given.
86  *
87  * <p>
88  * A property language files that need to be checked against can be passed the
89  * reference file can be set by {@link #checkFile(String)}.
90  *
91  * <p>
92  * The results can be retrieved by using the get* methods.
93  *
94  * @author sreindl
95  */

96 public class L10nInspector
97 {
98
99     /* regular expression matching stuff */
100     private static String JavaDoc COMMENT_TRANS = "^\\s*#(\\+|-)TRANS.*$";
101
102     private static String JavaDoc COMMENT_REGEX = "^\\s*(#.*)?$";
103
104     private static String JavaDoc COMMAND_REGEX = "^\\s*([^=\\s]+)\\s*=\\s*(.*)$";
105
106     private static Perl5Compiler compiler = new Perl5Compiler();
107
108     private static Perl5Matcher matcher = new Perl5Matcher();
109
110     private static Pattern commentPattern = null;
111
112     private static Pattern commandPattern = null;
113
114     private static Pattern transPattern = null;
115     
116     /* properties of reference file */
117     private Hashtable JavaDoc refProperties;
118
119     /* filename of reference file */
120     private String JavaDoc refFileName;
121
122     /* filename of file to be checked */
123     private String JavaDoc checkFileName;
124
125     /* statistical information */
126     private int linesRead = 0;
127
128     /* messages generated during parsing */
129     private List JavaDoc messages = null;
130     
131     /**
132      * Create a standard instance
133      */

134     public L10nInspector() throws MalformedPatternException
135     {
136         try
137         {
138             L10nIssueTemplates.reset();
139             commentPattern = compiler.compile(COMMENT_REGEX);
140             commandPattern = compiler.compile(COMMAND_REGEX);
141             transPattern = compiler.compile (COMMENT_TRANS);
142         } catch (MalformedPatternException exMP)
143         {
144             Log.get().fatal(exMP);
145             throw exMP; // rethrow
146
}
147         messages = new ArrayList JavaDoc();
148     }
149
150     /**
151      * Set the reference file to be used. This call resets all internal
152      * variables and resets all counters
153      *
154      * @param aRefFile
155      * The file to be used as a reference
156      *
157      * @throws IOException
158      * In case the file cannot be read.
159      */

160     public int setReference(String JavaDoc aRefFile) throws IOException JavaDoc
161     {
162         refFileName = aRefFile;
163
164         refProperties = new Hashtable JavaDoc();
165         File JavaDoc inFile = new File JavaDoc(refFileName);
166         if (!inFile.canRead())
167         {
168             throw new IOException JavaDoc("Cannot read reference file " + aRefFile);
169         }
170         loadReferenceFile(inFile);
171         return refProperties.size();
172     }
173
174     /**
175      * Function to return only the errors that occured during parsing
176      *
177      * @return Returns the errors.
178      */

179     public List JavaDoc getErrors()
180     {
181         List JavaDoc errs = new ArrayList JavaDoc(messages.size());
182         Iterator JavaDoc it = messages.iterator();
183
184         while (it.hasNext())
185         {
186             L10nMessage msg = (L10nMessage) it.next();
187             if (msg.getIssue().isError())
188             {
189                 errs.add(msg);
190             }
191         }
192         return errs;
193     }
194
195     /**
196      * Return a list of warnings generated during processing
197      *
198      * @return Returns the warnings.
199      */

200     public List JavaDoc getWarnings()
201     {
202         List JavaDoc warnings = new ArrayList JavaDoc(messages.size());
203         Iterator JavaDoc it = messages.iterator();
204
205         while (it.hasNext())
206         {
207             L10nMessage msg = (L10nMessage) it.next();
208             if (msg.getIssue().isWarning())
209             {
210                 warnings.add(msg);
211             }
212         }
213         return warnings;
214     }
215
216     /**
217      * Generate a list of information messages generated during processing.
218      *
219      * @return Returns the information messages.
220      */

221     public List JavaDoc getInfos()
222     {
223         List JavaDoc infos = new ArrayList JavaDoc(messages.size());
224         Iterator JavaDoc it = messages.iterator();
225
226         while (it.hasNext())
227         {
228             L10nMessage msg = (L10nMessage) it.next();
229             if (msg.getIssue().isInfo())
230             {
231                 infos.add(msg);
232             }
233         }
234         return infos;
235     }
236
237     /**
238      * Return the messages collected.
239      *
240      * @return messages
241      */

242     public List JavaDoc getMessages()
243     {
244         return messages;
245     }
246
247     /**
248      * Check if the parsing/checking resulted in errors.
249      *
250      * @return true if errors have been seen during loading/parsing
251      */

252     public boolean hasErrors()
253     {
254         return getErrors().size() > 0;
255     }
256
257     /**
258      * Check an language file.
259      *
260      * <p>
261      * TODO: Sophisticated exception handling
262      *
263      * @return Number of (unique)translation entries
264      */

265     public int checkFile(String JavaDoc filename) throws IOException JavaDoc
266     {
267         BufferedReader JavaDoc inStream = null;
268         String JavaDoc inLine;
269         Hashtable JavaDoc seen = new Hashtable JavaDoc();
270         int lineNo = 0;
271         boolean doNotTrans = false;
272
273         messages.clear();
274         checkFileName = filename;
275         try
276         {
277             inStream = new BufferedReader JavaDoc(new FileReader JavaDoc(filename));
278             while ((inLine = inStream.readLine()) != null)
279             {
280                 lineNo++;
281                 if (matcher.matches(inLine, transPattern))
282                 {
283                     MatchResult result = matcher.getMatch();
284                     if (result.group(1).equals("-"))
285                     {
286                         doNotTrans = true;
287                     }
288                 }
289                 else if (matcher.matches(inLine, commentPattern))
290                 {
291                     // skip comment lines
292
continue;
293                 }
294                 else if (matcher.contains(inLine, commandPattern))
295                 {
296                     // extract key and value and insert them into
297
// the reference pattern list
298
MatchResult result = matcher.getMatch();
299                     String JavaDoc key = result.group(1);
300                     String JavaDoc value = result.group(2);
301                     L10nKey l10nKey = new L10nKey(key, value, lineNo);
302                     if (value.indexOf('{') >= 0) {
303                         // handle attributes
304
try
305                         {
306                             MessageFormat JavaDoc fmt = new MessageFormat JavaDoc (value);
307                             int attributeCount = fmt.getFormats().length;
308                             l10nKey.setAttributeCount(attributeCount);
309                         }
310                         catch (IllegalArgumentException JavaDoc exIAE)
311                         {
312                             addMessage (lineNo, new IllegalPatternIssue(key), null);
313                             continue;
314                         }
315                     }
316                     l10nKey.setNoTrans(doNotTrans);
317                     doNotTrans = false;
318                     
319                     // we've seen this key
320
if (seen.contains(l10nKey))
321                     {
322                         // same entry in translation twice
323
L10nKey orig = (L10nKey) seen.get(l10nKey);
324                         if (orig.getValue().equals(l10nKey.getValue()))
325                         {
326                             // same entry with same translation -> info
327
addMessage (lineNo, new TranslatedTwiceIssue(key, orig.getLineNo()),
328                                     l10nKey);
329                         }
330                         else
331                         {
332                             // same key, different translation -> error
333
addMessage (lineNo, new TranslatedTwiceDiffIssue(key, orig.getLineNo()),
334                                     l10nKey);
335                         }
336                         seen.remove(orig); // remove original key
337
seen.put(l10nKey, l10nKey);
338                         continue; // do not create other errors here
339
}
340                     seen.put(l10nKey, l10nKey);
341                     L10nKey ref = (L10nKey) refProperties.get(key);
342                     if (ref == null)
343                     {
344                         // error: key not in reference
345
addMessage (lineNo, new NotInReferenceIssue (key), l10nKey);
346                     }
347                     else
348                     {
349                         if (ref.isNoTrans())
350                         {
351                             // This entry is not supposed to be translated
352
addMessage (lineNo, new NoTransAllowedIssue(key), l10nKey);
353                         }
354                         else if (ref.getValue().equals(value))
355                         {
356                             if (ref.isNeedTrans())
357                             {
358                                 // info: Key not found in translation set. but required
359
addMessage (lineNo, new TranslationRequiredIssue(key), ref);
360                             }
361                             else
362                             {
363                                 // not translated. Will only add the warning if the
364
// key has not been marked as NOTRANS in the target language
365
// bundle.
366
if (!l10nKey.isNoTrans())
367                                     addMessage (lineNo, new NotTranslatedIssue (key), l10nKey);
368                             }
369                         }
370                         else if (ref.getAttributeCount() != l10nKey.getAttributeCount())
371                         {
372                             // different number of attributes in reference and translation
373
addMessage (lineNo,
374                                     new DifferentAttributeCountIssue(key,
375                                                 l10nKey.getAttributeCount(),
376                                                 ref.getAttributeCount()),
377                                             l10nKey);
378                         }
379                     }
380                 } else
381                 {
382                     addMessage(lineNo, new CantParseLineIssue(inLine), null);
383                 }
384             }
385         } catch (IOException JavaDoc exIO)
386         {
387             Log.get().error(exIO);
388             exIO.printStackTrace();
389             // cleanup resources
390
refProperties.clear();
391             messages.clear();
392             throw exIO; // rethrow
393
} catch (Exception JavaDoc e)
394         {
395             Log.get().error(e);
396             e.printStackTrace();
397             // cleanup resources
398
refProperties.clear();
399             messages.clear();
400             throw new IOException JavaDoc(e.getMessage());
401         }
402
403         // look for missing messages
404
Iterator JavaDoc it = refProperties.keySet().iterator();
405         while (it.hasNext())
406         {
407             String JavaDoc key = (String JavaDoc) it.next();
408             L10nKey refKey = (L10nKey)refProperties.get(key);
409             if (!seen.contains(refKey))
410             {
411                 if (refKey.isNeedTrans())
412                 {
413                     // info: Key not found in translation set. but required
414
addMessage (-1, new TranslationRequiredIssue(key), refKey);
415                 }
416                 else
417                 {
418                     // info: Key not found in translation set
419
addMessage (-1, new TranslationMissingIssue (key), refKey);
420                 }
421             }
422         }
423         this.linesRead = lineNo;
424         return seen.size();
425     }
426
427     /* private functions */
428
429     /**
430      * Load a the reference file.
431      *
432      * <p>
433      * TODO: Sophisticated exception handling
434      */

435     private void loadReferenceFile(File JavaDoc inFile) throws IOException JavaDoc
436     {
437         BufferedReader JavaDoc inStream = null;
438         String JavaDoc inLine;
439         int lineNo = 0;
440         boolean transNeeded = false;
441         boolean doNotTrans = false;
442         
443         try
444         {
445             inStream = new BufferedReader JavaDoc(new FileReader JavaDoc(inFile));
446             while ((inLine = inStream.readLine()) != null)
447             {
448                 lineNo++;
449                 if (matcher.matches(inLine, transPattern))
450                 {
451                     MatchResult result = matcher.getMatch();
452                     if (result.group(1).equals("+"))
453                     {
454                         transNeeded = true;
455                     }
456                     else
457                     {
458                         doNotTrans = true;
459                     }
460                 }
461                 else if (matcher.matches(inLine, commentPattern))
462                 {
463                     // skip comment lines
464
continue;
465                 }
466                 else if (matcher.contains(inLine, commandPattern))
467                 {
468                     // extract key and value and insert them into
469
// the reference pattern list
470
MatchResult result = matcher.getMatch();
471                     String JavaDoc key = result.group(1);
472                     String JavaDoc value = result.group(2);
473                     L10nKey l10nKey = new L10nKey(key, value, lineNo);
474                     if (value.indexOf('{') >= 0)
475                     {
476                         // handle attributes
477
try
478                         {
479                             MessageFormat JavaDoc fmt = new MessageFormat JavaDoc (value);
480                             int attributeCount = fmt.getFormats().length;
481                             l10nKey.setAttributeCount(attributeCount);
482                         }
483                         catch (IllegalArgumentException JavaDoc exIAE)
484                         {
485                             addMessage (lineNo, new IllegalPatternIssue(key), null);
486                             continue;
487                         }
488                     }
489                     l10nKey.setNeedTrans(transNeeded);
490                     l10nKey.setNoTrans(doNotTrans);
491                     // reset values
492
transNeeded = false;
493                     doNotTrans = false;
494                     if (refProperties.get(key) != null)
495                     {
496                         // error: key already there
497
L10nKey orig = (L10nKey) refProperties.get(key);
498                         if (orig.getValue().equals(l10nKey.getValue()))
499                         {
500                             addMessage(lineNo, new DefinedTwiceIssue(key, orig.getLineNo()),
501                                     l10nKey);
502                         } else {
503                             // even worse: same key with different values
504
addMessage(lineNo, new DefinedTwiceIssue (key, orig.getLineNo()),
505                                     l10nKey);
506                         }
507                         continue;
508                     }
509                     // add value
510
refProperties.put(key, l10nKey);
511                 } else
512                 {
513                     addMessage(lineNo, new CantParseLineIssue (inLine), null);
514                 }
515             }
516         } catch (IOException JavaDoc exIO)
517         {
518             exIO.printStackTrace();
519             Log.get().error(exIO);
520             // cleanup resources
521
refProperties.clear();
522             messages.clear();
523             throw exIO; // rethrow
524
} catch (Exception JavaDoc e)
525         {
526             Log.get().error(e);
527             // cleanup resources
528
e.printStackTrace();
529             refProperties.clear();
530             messages.clear();
531             throw new IOException JavaDoc(e.getMessage());
532         }
533         this.linesRead = lineNo;
534     }
535
536     /* Add an error message */
537     private void addMessage(int lineNo, L10nIssue issue, L10nKey l10nKey)
538     {
539         L10nMessage err = new L10nMessage(lineNo, issue);
540         if (l10nKey != null)
541         {
542             err.setL10nObject(l10nKey);
543         }
544         messages.add(err);
545     }
546
547     /**
548      * Main line.
549      *
550      * <p>
551      * The pathes here are hardcoded, please change accordingly if you want to
552      * use the test main code
553      */

554     public static void main(String JavaDoc[] args)
555     {
556         L10nInspector ins = null;
557         System.err.println ("This is only used for internal tests");
558         try
559         {
560             ins = new L10nInspector();
561         } catch (MalformedPatternException exMP)
562         {
563             System.exit(1); // we cannot continue
564
}
565         try
566         {
567             File JavaDoc f = new File JavaDoc(".");
568             System.err.println("We are here:" + f.getAbsolutePath());
569             ins.setReference("src/conf/classes/ScarabBundle_en.properties");
570         } catch (IOException JavaDoc exIO)
571         {
572             exIO.printStackTrace();
573             System.exit(1);
574         }
575         if (ins.getErrors().size() > 0)
576         {
577             Iterator JavaDoc it = ins.getErrors().iterator();
578             while (it.hasNext())
579             {
580                 L10nMessage data = (L10nMessage) it.next();
581                 System.err.println("E " + data.getLineNumber() + ": "
582                         + data.getMessageText());
583             }
584         }
585         if (ins.getWarnings().size() > 0)
586         {
587             Iterator JavaDoc it = ins.getWarnings().iterator();
588             while (it.hasNext())
589             {
590                 L10nMessage data = (L10nMessage) it.next();
591                 System.err.println("W " + data.getLineNumber() + ": "
592                         + data.getMessageText());
593             }
594         }
595         if (ins.hasErrors())
596         {
597             //System.exit (1);
598
}
599         System.out.println("--- checking de");
600         try
601         {
602             int lines = ins
603                     .checkFile("src/conf/classes/ScarabBundle_es.properties");
604         } catch (Exception JavaDoc e)
605         {
606             e.printStackTrace();
607             System.exit(1);
608         }
609         List JavaDoc msgs = ins.getMessages();
610         Iterator JavaDoc it = msgs.iterator();
611         while (it.hasNext())
612         {
613             L10nMessage msg = (L10nMessage) it.next();
614             if (msg.getIssue().isError())
615             {
616                 System.out.print('E');
617             } else if (msg.getIssue().isWarning())
618             {
619                 System.out.print('W');
620             } else if (msg.getIssue().isInfo())
621             {
622                 System.out.print('I');
623             }
624             if (msg.getLineNumber() > 0)
625             {
626                 System.out.print(" " + msg.getLineNumber());
627             }
628             System.out.println(": " + msg.getMessageText());
629         }
630     }
631 }
632
Popular Tags