View Javadoc

1   /*
2    *   Copyright (C) Christian Schulte, 2005-206
3    *   All rights reserved.
4    *
5    *   Redistribution and use in source and binary forms, with or without
6    *   modification, are permitted provided that the following conditions
7    *   are met:
8    *
9    *     o Redistributions of source code must retain the above copyright
10   *       notice, this list of conditions and the following disclaimer.
11   *
12   *     o Redistributions in binary form must reproduce the above copyright
13   *       notice, this list of conditions and the following disclaimer in
14   *       the documentation and/or other materials provided with the
15   *       distribution.
16   *
17   *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
18   *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19   *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
20   *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
21   *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22   *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23   *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24   *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25   *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26   *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27   *
28   *   $JOMC: ResourceFileProcessor.java 3868 2011-10-14 13:23:09Z schulte2005 $
29   *
30   */
31  package org.jomc.tools;
32  
33  import java.io.File;
34  import java.io.FileOutputStream;
35  import java.io.IOException;
36  import java.io.OutputStream;
37  import java.text.MessageFormat;
38  import java.util.HashMap;
39  import java.util.Locale;
40  import java.util.Map;
41  import java.util.Properties;
42  import java.util.ResourceBundle;
43  import java.util.logging.Level;
44  import org.apache.velocity.VelocityContext;
45  import org.jomc.model.Implementation;
46  import org.jomc.model.Message;
47  import org.jomc.model.Messages;
48  import org.jomc.model.Module;
49  import org.jomc.model.Specification;
50  import org.jomc.model.Text;
51  
52  /**
53   * Processes resource files.
54   *
55   * <p><b>Use Cases:</b><br/><ul>
56   * <li>{@link #writeResourceBundleResourceFiles(File) }</li>
57   * <li>{@link #writeResourceBundleResourceFiles(Module, File) }</li>
58   * <li>{@link #writeResourceBundleResourceFiles(Specification, File) }</li>
59   * <li>{@link #writeResourceBundleResourceFiles(Implementation, File) }</li>
60   * </ul></p>
61   *
62   * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
63   * @version $JOMC: ResourceFileProcessor.java 3868 2011-10-14 13:23:09Z schulte2005 $
64   *
65   * @see #getModules()
66   */
67  public class ResourceFileProcessor extends JomcTool
68  {
69  
70      /** The language of the default language properties file of generated resource bundle resources. */
71      private Locale resourceBundleDefaultLocale;
72  
73      /** Creates a new {@code ResourceFileProcessor} instance. */
74      public ResourceFileProcessor()
75      {
76          super();
77      }
78  
79      /**
80       * Creates a new {@code ResourceFileProcessor} instance taking a {@code ResourceFileProcessor} instance to
81       * initialize the instance with.
82       *
83       * @param tool The instance to initialize the new instance with.
84       *
85       * @throws NullPointerException if {@code tool} is {@code null}.
86       * @throws IOException if copying {@code tool} fails.
87       */
88      public ResourceFileProcessor( final ResourceFileProcessor tool ) throws IOException
89      {
90          super( tool );
91          this.resourceBundleDefaultLocale = tool.resourceBundleDefaultLocale;
92      }
93  
94      /**
95       * Gets the language of the default language properties file of generated resource bundle resource files.
96       *
97       * @return The language of the default language properties file of generated resource bundle resource files.
98       *
99       * @see #setResourceBundleDefaultLocale(java.util.Locale)
100      */
101     public final Locale getResourceBundleDefaultLocale()
102     {
103         if ( this.resourceBundleDefaultLocale == null )
104         {
105             this.resourceBundleDefaultLocale = Locale.ENGLISH;
106 
107             if ( this.isLoggable( Level.CONFIG ) )
108             {
109                 this.log( Level.CONFIG,
110                           getMessage( "defaultResourceBundleDefaultLocale", this.resourceBundleDefaultLocale ), null );
111 
112             }
113         }
114 
115         return this.resourceBundleDefaultLocale;
116     }
117 
118     /**
119      * Sets the language of the default language properties file of generated resource bundle resource files.
120      *
121      * @param value The language of the default language properties file of generated resource bundle resource files.
122      *
123      * @see #getResourceBundleDefaultLocale()
124      */
125     public final void setResourceBundleDefaultLocale( final Locale value )
126     {
127         this.resourceBundleDefaultLocale = value;
128     }
129 
130     /**
131      * Writes resource bundle resource files of the modules of the instance to a given directory.
132      *
133      * @param resourcesDirectory The directory to write resource bundle resource files to.
134      *
135      * @throws NullPointerException if {@code resourcesDirectory} is {@code null}.
136      * @throws IOException if writing resource bundle resource files fails.
137      *
138      * @see #writeResourceBundleResourceFiles(org.jomc.model.Module, java.io.File)
139      */
140     public void writeResourceBundleResourceFiles( final File resourcesDirectory ) throws IOException
141     {
142         if ( resourcesDirectory == null )
143         {
144             throw new NullPointerException( "resourcesDirectory" );
145         }
146 
147         for ( int i = 0, s0 = this.getModules().getModule().size(); i < s0; i++ )
148         {
149             this.writeResourceBundleResourceFiles( this.getModules().getModule().get( i ), resourcesDirectory );
150         }
151     }
152 
153     /**
154      * Writes resource bundle resource files of a given module from the modules of the instance to a given directory.
155      *
156      * @param module The module to process.
157      * @param resourcesDirectory The directory to write resource bundle resource files to.
158      *
159      * @throws NullPointerException if {@code module} or {@code resourcesDirectory} is {@code null}.
160      * @throws IOException if writing resource bundle resource files fails.
161      *
162      * @see #writeResourceBundleResourceFiles(org.jomc.model.Specification, java.io.File)
163      * @see #writeResourceBundleResourceFiles(org.jomc.model.Implementation, java.io.File)
164      */
165     public void writeResourceBundleResourceFiles( final Module module, final File resourcesDirectory )
166         throws IOException
167     {
168         if ( module == null )
169         {
170             throw new NullPointerException( "module" );
171         }
172         if ( resourcesDirectory == null )
173         {
174             throw new NullPointerException( "resourcesDirectory" );
175         }
176 
177         assert this.getModules().getModule( module.getName() ) != null : "Module '" + module.getName() + "' not found.";
178 
179         if ( module.getSpecifications() != null )
180         {
181             for ( int i = 0, s0 = module.getSpecifications().getSpecification().size(); i < s0; i++ )
182             {
183                 this.writeResourceBundleResourceFiles( module.getSpecifications().getSpecification().get( i ),
184                                                        resourcesDirectory );
185 
186             }
187         }
188 
189         if ( module.getImplementations() != null )
190         {
191             for ( int i = 0, s0 = module.getImplementations().getImplementation().size(); i < s0; i++ )
192             {
193                 this.writeResourceBundleResourceFiles( module.getImplementations().getImplementation().get( i ),
194                                                        resourcesDirectory );
195 
196             }
197         }
198     }
199 
200     /**
201      * Writes resource bundle resource files of a given specification from the modules of the instance to a directory.
202      *
203      * @param specification The specification to process.
204      * @param resourcesDirectory The directory to write resource bundle resource files to.
205      *
206      * @throws NullPointerException if {@code specification} or {@code resourcesDirectory} is {@code null}.
207      * @throws IOException if writing resource bundle resource files fails.
208      *
209      * @see #getResourceBundleResources(org.jomc.model.Specification)
210      */
211     public void writeResourceBundleResourceFiles( final Specification specification, final File resourcesDirectory )
212         throws IOException
213     {
214         if ( specification == null )
215         {
216             throw new NullPointerException( "implementation" );
217         }
218         if ( resourcesDirectory == null )
219         {
220             throw new NullPointerException( "resourcesDirectory" );
221         }
222 
223         assert this.getModules().getSpecification( specification.getIdentifier() ) != null :
224             "Specification '" + specification.getIdentifier() + "' not found.";
225 
226         if ( specification.isClassDeclaration() )
227         {
228             if ( !resourcesDirectory.isDirectory() )
229             {
230                 throw new IOException( getMessage( "directoryNotFound", resourcesDirectory.getAbsolutePath() ) );
231             }
232 
233             this.assertValidTemplates( specification );
234 
235             final String bundlePath =
236                 this.getJavaTypeName( specification, true ).replace( '.', File.separatorChar );
237 
238             this.writeResourceBundleResourceFiles(
239                 this.getResourceBundleResources( specification ), resourcesDirectory, bundlePath );
240 
241         }
242     }
243 
244     /**
245      * Writes resource bundle resource files of a given implementation from the modules of the instance to a directory.
246      *
247      * @param implementation The implementation to process.
248      * @param resourcesDirectory The directory to write resource bundle resource files to.
249      *
250      * @throws NullPointerException if {@code implementation} or {@code resourcesDirectory} is {@code null}.
251      * @throws IOException if writing resource bundle resource files fails.
252      *
253      * @see #getResourceBundleResources(org.jomc.model.Implementation)
254      */
255     public void writeResourceBundleResourceFiles( final Implementation implementation, final File resourcesDirectory )
256         throws IOException
257     {
258         if ( implementation == null )
259         {
260             throw new NullPointerException( "implementation" );
261         }
262         if ( resourcesDirectory == null )
263         {
264             throw new NullPointerException( "resourcesDirectory" );
265         }
266 
267         assert this.getModules().getImplementation( implementation.getIdentifier() ) != null :
268             "Implementation '" + implementation.getIdentifier() + "' not found.";
269 
270         if ( implementation.isClassDeclaration() )
271         {
272             if ( !resourcesDirectory.isDirectory() )
273             {
274                 throw new IOException( getMessage( "directoryNotFound", resourcesDirectory.getAbsolutePath() ) );
275             }
276 
277             this.assertValidTemplates( implementation );
278 
279             final String bundlePath =
280                 this.getJavaTypeName( implementation, true ).replace( '.', File.separatorChar );
281 
282             this.writeResourceBundleResourceFiles(
283                 this.getResourceBundleResources( implementation ), resourcesDirectory, bundlePath );
284 
285         }
286     }
287 
288     /**
289      * Gets resource bundle properties resources of a given specification.
290      *
291      * @param specification The specification to get resource bundle properties resources of.
292      *
293      * @return Resource bundle properties resources of {@code specification}.
294      *
295      * @throws NullPointerException if {@code specification} is {@code null}.
296      * @throws IOException if getting the resource bundle properties resources fails.
297      */
298     public Map<Locale, Properties> getResourceBundleResources( final Specification specification )
299         throws IOException
300     {
301         if ( specification == null )
302         {
303             throw new NullPointerException( "specification" );
304         }
305 
306         assert this.getModules().getSpecification( specification.getIdentifier() ) != null :
307             "Specification '" + specification.getIdentifier() + "' not found.";
308 
309         return new HashMap<Locale, Properties>();
310     }
311 
312     /**
313      * Gets resource bundle properties resources of a given implementation.
314      *
315      * @param implementation The implementation to get resource bundle properties resources of.
316      *
317      * @return Resource bundle properties resources of {@code implementation}.
318      *
319      * @throws NullPointerException if {@code implementation} is {@code null}.
320      * @throws IOException if getting the resource bundle properties resources fails.
321      */
322     public Map<Locale, Properties> getResourceBundleResources( final Implementation implementation )
323         throws IOException
324     {
325         if ( implementation == null )
326         {
327             throw new NullPointerException( "implementation" );
328         }
329 
330         assert this.getModules().getImplementation( implementation.getIdentifier() ) != null :
331             "Implementation '" + implementation.getIdentifier() + "' not found.";
332 
333         final Map<Locale, java.util.Properties> properties = new HashMap<Locale, java.util.Properties>( 10 );
334         final Messages messages = this.getModules().getMessages( implementation.getIdentifier() );
335 
336         if ( messages != null )
337         {
338             for ( int i = 0, s0 = messages.getMessage().size(); i < s0; i++ )
339             {
340                 final Message message = messages.getMessage().get( i );
341 
342                 if ( message.getTemplate() != null )
343                 {
344                     for ( int j = 0, s1 = message.getTemplate().getText().size(); j < s1; j++ )
345                     {
346                         final Text text = message.getTemplate().getText().get( j );
347                         final Locale locale = new Locale( text.getLanguage().toLowerCase() );
348                         Properties bundleProperties = properties.get( locale );
349 
350                         if ( bundleProperties == null )
351                         {
352                             bundleProperties = new Properties();
353                             properties.put( locale, bundleProperties );
354                         }
355 
356                         bundleProperties.setProperty( message.getName(), text.getValue() );
357                     }
358                 }
359             }
360         }
361 
362         return properties;
363     }
364 
365     private void writeResourceBundleResourceFiles( final Map<Locale, Properties> resources,
366                                                    final File resourcesDirectory, final String bundlePath )
367         throws IOException
368     {
369         if ( resources == null )
370         {
371             throw new NullPointerException( "resources" );
372         }
373         if ( resourcesDirectory == null )
374         {
375             throw new NullPointerException( "resourcesDirectory" );
376         }
377         if ( bundlePath == null )
378         {
379             throw new NullPointerException( "bundlePath" );
380         }
381 
382         Properties defProperties = null;
383         Properties fallbackProperties = null;
384 
385         final VelocityContext ctx = this.getVelocityContext();
386         final String toolName = ctx.get( "toolName" ).toString();
387         final String toolVersion = ctx.get( "toolVersion" ).toString();
388         final String toolUrl = ctx.get( "toolUrl" ).toString();
389 
390         for ( Map.Entry<Locale, Properties> e : resources.entrySet() )
391         {
392             final String language = e.getKey().getLanguage().toLowerCase();
393             final Properties p = e.getValue();
394             final File file = new File( resourcesDirectory, bundlePath + "_" + language + ".properties" );
395 
396             if ( this.getResourceBundleDefaultLocale().getLanguage().equalsIgnoreCase( language ) )
397             {
398                 defProperties = p;
399             }
400 
401             fallbackProperties = p;
402 
403             if ( !file.getParentFile().exists() && !file.getParentFile().mkdirs() )
404             {
405                 throw new IOException( getMessage( "failedCreatingDirectory",
406                                                    file.getParentFile().getAbsolutePath() ) );
407 
408             }
409 
410             if ( this.isLoggable( Level.INFO ) )
411             {
412                 this.log( Level.INFO, getMessage( "writing", file.getCanonicalPath() ), null );
413             }
414 
415             OutputStream out = null;
416             boolean suppressExceptionOnClose = true;
417             try
418             {
419                 out = new FileOutputStream( file );
420                 p.store( out, toolName + ' ' + toolVersion + " - See " + toolUrl );
421                 suppressExceptionOnClose = false;
422             }
423             finally
424             {
425                 try
426                 {
427                     if ( out != null )
428                     {
429                         out.close();
430                     }
431                 }
432                 catch ( final IOException ex )
433                 {
434                     if ( suppressExceptionOnClose )
435                     {
436                         this.log( Level.SEVERE, getMessage( ex ), ex );
437                     }
438                     else
439                     {
440                         throw ex;
441                     }
442                 }
443             }
444         }
445 
446         if ( defProperties == null )
447         {
448             defProperties = fallbackProperties;
449         }
450 
451         if ( defProperties != null )
452         {
453             final File file = new File( resourcesDirectory, bundlePath + ".properties" );
454             if ( !file.getParentFile().exists() && !file.getParentFile().mkdirs() )
455             {
456                 throw new IOException( getMessage( "failedCreatingDirectory",
457                                                    file.getParentFile().getAbsolutePath() ) );
458 
459             }
460 
461             if ( this.isLoggable( Level.INFO ) )
462             {
463                 this.log( Level.INFO, getMessage( "writing", file.getCanonicalPath() ), null );
464             }
465 
466             OutputStream out = null;
467             boolean suppressExceptionOnClose = true;
468             try
469             {
470                 out = new FileOutputStream( file );
471                 defProperties.store( out, toolName + ' ' + toolVersion + " - See " + toolUrl );
472                 suppressExceptionOnClose = false;
473             }
474             finally
475             {
476                 try
477                 {
478                     if ( out != null )
479                     {
480                         out.close();
481                     }
482                 }
483                 catch ( final IOException e )
484                 {
485                     if ( suppressExceptionOnClose )
486                     {
487                         this.log( Level.SEVERE, getMessage( e ), e );
488                     }
489                     else
490                     {
491                         throw e;
492                     }
493                 }
494             }
495         }
496     }
497 
498     private void assertValidTemplates( final Specification specification )
499     {
500         if ( specification == null )
501         {
502             throw new NullPointerException( "specification" );
503         }
504     }
505 
506     private void assertValidTemplates( final Implementation implementation )
507     {
508         if ( implementation == null )
509         {
510             throw new NullPointerException( "implementation" );
511         }
512 
513         final Messages messages = this.getModules().getMessages( implementation.getIdentifier() );
514 
515         if ( messages != null )
516         {
517             for ( int i = messages.getMessage().size() - 1; i >= 0; i-- )
518             {
519                 final Message m = messages.getMessage().get( i );
520 
521                 if ( m.getTemplate() != null )
522                 {
523                     for ( int j = m.getTemplate().getText().size() - 1; j >= 0; j-- )
524                     {
525                         new MessageFormat( m.getTemplate().getText().get( j ).getValue() );
526                     }
527                 }
528             }
529         }
530     }
531 
532     private static String getMessage( final String key, final Object... arguments )
533     {
534         if ( key == null )
535         {
536             throw new NullPointerException( "key" );
537         }
538 
539         return MessageFormat.format( ResourceBundle.getBundle(
540             ResourceFileProcessor.class.getName().replace( '.', '/' ) ).getString( key ), arguments );
541 
542     }
543 
544     private static String getMessage( final Throwable t )
545     {
546         return t != null ? t.getMessage() != null ? t.getMessage() : getMessage( t.getCause() ) : null;
547     }
548 
549 }