001    /*
002     *   Copyright (c) 2009 The JOMC Project
003     *   Copyright (c) 2005 Christian Schulte <schulte2005@users.sourceforge.net>
004     *   All rights reserved.
005     *
006     *   Redistribution and use in source and binary forms, with or without
007     *   modification, are permitted provided that the following conditions
008     *   are met:
009     *
010     *     o Redistributions of source code must retain the above copyright
011     *       notice, this list of conditions and the following disclaimer.
012     *
013     *     o Redistributions in binary form must reproduce the above copyright
014     *       notice, this list of conditions and the following disclaimer in
015     *       the documentation and/or other materials provided with the
016     *       distribution.
017     *
018     *   THIS SOFTWARE IS PROVIDED BY THE JOMC PROJECT AND CONTRIBUTORS "AS IS"
019     *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020     *   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021     *   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JOMC PROJECT OR
022     *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023     *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024     *   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025     *   OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026     *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
027     *   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
028     *   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029     *
030     *   $Id: ResourceFileProcessor.java 2179 2010-06-28 17:49:49Z schulte2005 $
031     *
032     */
033    package org.jomc.tools;
034    
035    import java.io.File;
036    import java.io.FileOutputStream;
037    import java.io.IOException;
038    import java.io.OutputStream;
039    import java.text.MessageFormat;
040    import java.util.HashMap;
041    import java.util.Locale;
042    import java.util.Map;
043    import java.util.Properties;
044    import java.util.ResourceBundle;
045    import java.util.logging.Level;
046    import org.apache.velocity.VelocityContext;
047    import org.jomc.model.Implementation;
048    import org.jomc.model.Message;
049    import org.jomc.model.Messages;
050    import org.jomc.model.Module;
051    import org.jomc.model.Specification;
052    import org.jomc.model.Text;
053    
054    /**
055     * Processes resource files.
056     *
057     * <p><b>Use cases</b><br/><ul>
058     * <li>{@link #writeResourceBundleResourceFiles(File) }</li>
059     * <li>{@link #writeResourceBundleResourceFiles(Module, File) }</li>
060     * <li>{@link #writeResourceBundleResourceFiles(Specification, File) }</li>
061     * <li>{@link #writeResourceBundleResourceFiles(Implementation, File) }</li>
062     * </ul></p>
063     *
064     * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
065     * @version $Id: ResourceFileProcessor.java 2179 2010-06-28 17:49:49Z schulte2005 $
066     *
067     * @see #getModules()
068     */
069    public class ResourceFileProcessor extends JomcTool
070    {
071    
072        /** The language of the default language properties file of generated resource bundle resources. */
073        private Locale resourceBundleDefaultLocale;
074    
075        /** Creates a new {@code ResourceFileProcessor} instance. */
076        public ResourceFileProcessor()
077        {
078            super();
079        }
080    
081        /**
082         * Creates a new {@code ResourceFileProcessor} instance taking a {@code ResourceFileProcessor} instance to
083         * initialize the instance with.
084         *
085         * @param tool The instance to initialize the new instance with.
086         *
087         * @throws NullPointerException if {@code tool} is {@code null}.
088         * @throws IOException if copying {@code tool} fails.
089         */
090        public ResourceFileProcessor( final ResourceFileProcessor tool ) throws IOException
091        {
092            super( tool );
093            this.resourceBundleDefaultLocale = tool.resourceBundleDefaultLocale;
094        }
095    
096        /**
097         * Gets the language of the default language properties file of generated resource bundle resource files.
098         *
099         * @return The language of the default language properties file of generated resource bundle resource files.
100         *
101         * @see #setResourceBundleDefaultLocale(java.util.Locale)
102         */
103        public final Locale getResourceBundleDefaultLocale()
104        {
105            if ( this.resourceBundleDefaultLocale == null )
106            {
107                this.resourceBundleDefaultLocale = Locale.getDefault();
108    
109                if ( this.isLoggable( Level.CONFIG ) )
110                {
111                    this.log( Level.CONFIG, getMessage( "resourceBundleDefaultLocale", this.getClass().getName(),
112                                                        this.resourceBundleDefaultLocale ), null );
113    
114                }
115            }
116    
117            return this.resourceBundleDefaultLocale;
118        }
119    
120        /**
121         * Sets the language of the default language properties file of generated resource bundle resource files.
122         *
123         * @param value The language of the default language properties file of generated resource bundle resource files.
124         *
125         * @see #getResourceBundleDefaultLocale()
126         */
127        public final void setResourceBundleDefaultLocale( final Locale value )
128        {
129            this.resourceBundleDefaultLocale = value;
130        }
131    
132        /**
133         * Writes resource bundle resource files of the modules of the instance to a given directory.
134         *
135         * @param resourcesDirectory The directory to write resource bundle resource files to.
136         *
137         * @throws NullPointerException if {@code resourcesDirectory} is {@code null}.
138         * @throws IOException if writing resource bundle resource files fails.
139         *
140         * @see #writeResourceBundleResourceFiles(org.jomc.model.Module, java.io.File)
141         */
142        public void writeResourceBundleResourceFiles( final File resourcesDirectory ) throws IOException
143        {
144            if ( resourcesDirectory == null )
145            {
146                throw new NullPointerException( "resourcesDirectory" );
147            }
148    
149            for ( Module m : this.getModules().getModule() )
150            {
151                this.writeResourceBundleResourceFiles( m, resourcesDirectory );
152            }
153        }
154    
155        /**
156         * Writes resource bundle resource files of a given module from the modules of the instance to a given directory.
157         *
158         * @param module The module to process.
159         * @param resourcesDirectory The directory to write resource bundle resource files to.
160         *
161         * @throws NullPointerException if {@code module} or {@code resourcesDirectory} is {@code null}.
162         * @throws IOException if writing resource bundle resource files fails.
163         *
164         * @see #writeResourceBundleResourceFiles(org.jomc.model.Specification, java.io.File)
165         * @see #writeResourceBundleResourceFiles(org.jomc.model.Implementation, java.io.File)
166         */
167        public void writeResourceBundleResourceFiles( final Module module, final File resourcesDirectory )
168            throws IOException
169        {
170            if ( module == null )
171            {
172                throw new NullPointerException( "module" );
173            }
174            if ( resourcesDirectory == null )
175            {
176                throw new NullPointerException( "resourcesDirectory" );
177            }
178    
179            assert this.getModules().getModule( module.getName() ) != null : "Module '" + module.getName() + "' not found.";
180    
181            if ( module.getSpecifications() != null )
182            {
183                for ( Specification s : module.getSpecifications().getSpecification() )
184                {
185                    this.writeResourceBundleResourceFiles( s, resourcesDirectory );
186                }
187            }
188    
189            if ( module.getImplementations() != null )
190            {
191                for ( Implementation i : module.getImplementations().getImplementation() )
192                {
193                    this.writeResourceBundleResourceFiles( i, resourcesDirectory );
194                }
195            }
196        }
197    
198        /**
199         * Writes resource bundle resource files of a given specification from the modules of the instance to a directory.
200         *
201         * @param specification The specification to process.
202         * @param resourcesDirectory The directory to write resource bundle resource files to.
203         *
204         * @throws NullPointerException if {@code specification} or {@code resourcesDirectory} is {@code null}.
205         * @throws IOException if writing resource bundle resource files fails.
206         *
207         * @see #getResourceBundleResources(org.jomc.model.Specification)
208         */
209        public void writeResourceBundleResourceFiles( final Specification specification, final File resourcesDirectory )
210            throws IOException
211        {
212            if ( specification == null )
213            {
214                throw new NullPointerException( "implementation" );
215            }
216            if ( resourcesDirectory == null )
217            {
218                throw new NullPointerException( "resourcesDirectory" );
219            }
220    
221            assert this.getModules().getSpecification( specification.getIdentifier() ) != null :
222                "Specification '" + specification.getIdentifier() + "' not found.";
223    
224            if ( specification.isClassDeclaration() )
225            {
226                this.assertValidTemplates( specification );
227    
228                final String bundlePath =
229                    this.getJavaTypeName( specification, true ).replace( '.', File.separatorChar );
230    
231                this.writeResourceBundleResourceFiles(
232                    this.getResourceBundleResources( specification ), resourcesDirectory, bundlePath );
233    
234            }
235        }
236    
237        /**
238         * Writes resource bundle resource files of a given implementation from the modules of the instance to a directory.
239         *
240         * @param implementation The implementation to process.
241         * @param resourcesDirectory The directory to write resource bundle resource files to.
242         *
243         * @throws NullPointerException if {@code implementation} or {@code resourcesDirectory} is {@code null}.
244         * @throws IOException if writing resource bundle resource files fails.
245         *
246         * @see #getResourceBundleResources(org.jomc.model.Implementation)
247         */
248        public void writeResourceBundleResourceFiles( final Implementation implementation, final File resourcesDirectory )
249            throws IOException
250        {
251            if ( implementation == null )
252            {
253                throw new NullPointerException( "implementation" );
254            }
255            if ( resourcesDirectory == null )
256            {
257                throw new NullPointerException( "resourcesDirectory" );
258            }
259    
260            assert this.getModules().getImplementation( implementation.getIdentifier() ) != null :
261                "Implementation '" + implementation.getIdentifier() + "' not found.";
262    
263            if ( implementation.isClassDeclaration() )
264            {
265                this.assertValidTemplates( implementation );
266    
267                final String bundlePath =
268                    this.getJavaTypeName( implementation, true ).replace( '.', File.separatorChar );
269    
270                this.writeResourceBundleResourceFiles(
271                    this.getResourceBundleResources( implementation ), resourcesDirectory, bundlePath );
272    
273            }
274        }
275    
276        /**
277         * Gets resource bundle properties resources of a given specification.
278         *
279         * @param specification The specification to get resource bundle properties resources of.
280         *
281         * @return Resource bundle properties resources of {@code specification}.
282         *
283         * @throws NullPointerException if {@code specification} is {@code null}.
284         * @throws IOException if getting the resource bundle properties resources fails.
285         */
286        public Map<Locale, Properties> getResourceBundleResources( final Specification specification )
287            throws IOException
288        {
289            if ( specification == null )
290            {
291                throw new NullPointerException( "specification" );
292            }
293    
294            assert this.getModules().getSpecification( specification.getIdentifier() ) != null :
295                "Specification '" + specification.getIdentifier() + "' not found.";
296    
297            return new HashMap<Locale, java.util.Properties>();
298        }
299    
300        /**
301         * Gets resource bundle properties resources of a given implementation.
302         *
303         * @param implementation The implementation to get resource bundle properties resources of.
304         *
305         * @return Resource bundle properties resources of {@code implementation}.
306         *
307         * @throws NullPointerException if {@code implementation} is {@code null}.
308         * @throws IOException if getting the resource bundle properties resources fails.
309         */
310        public Map<Locale, Properties> getResourceBundleResources( final Implementation implementation )
311            throws IOException
312        {
313            if ( implementation == null )
314            {
315                throw new NullPointerException( "implementation" );
316            }
317    
318            assert this.getModules().getImplementation( implementation.getIdentifier() ) != null :
319                "Implementation '" + implementation.getIdentifier() + "' not found.";
320    
321            final Map<Locale, java.util.Properties> properties = new HashMap<Locale, java.util.Properties>( 10 );
322            final Messages messages = this.getModules().getMessages( implementation.getIdentifier() );
323    
324            if ( messages != null )
325            {
326                for ( Message message : messages.getMessage() )
327                {
328                    if ( message.getTemplate() != null )
329                    {
330                        for ( Text text : message.getTemplate().getText() )
331                        {
332                            final Locale locale = new Locale( text.getLanguage().toLowerCase() );
333                            Properties bundleProperties = properties.get( locale );
334    
335                            if ( bundleProperties == null )
336                            {
337                                bundleProperties = new Properties();
338                                properties.put( locale, bundleProperties );
339                            }
340    
341                            bundleProperties.setProperty( message.getName(), text.getValue() );
342                        }
343                    }
344                }
345            }
346    
347            return properties;
348        }
349    
350        private void writeResourceBundleResourceFiles( final Map<Locale, Properties> resources,
351                                                       final File resourcesDirectory, final String bundlePath )
352            throws IOException
353        {
354            if ( resources == null )
355            {
356                throw new NullPointerException( "resources" );
357            }
358            if ( resourcesDirectory == null )
359            {
360                throw new NullPointerException( "resourcesDirectory" );
361            }
362            if ( bundlePath == null )
363            {
364                throw new NullPointerException( "bundlePath" );
365            }
366    
367            Properties defProperties = null;
368            Properties fallbackProperties = null;
369    
370            final VelocityContext ctx = this.getVelocityContext();
371            final String toolName = ctx.get( "toolName" ).toString();
372            final String toolVersion = ctx.get( "toolVersion" ).toString();
373            final String toolUrl = ctx.get( "toolUrl" ).toString();
374    
375            for ( Map.Entry<Locale, Properties> e : resources.entrySet() )
376            {
377                final String language = e.getKey().getLanguage().toLowerCase();
378                final Properties p = e.getValue();
379                final File file = new File( resourcesDirectory, bundlePath + "_" + language + ".properties" );
380    
381                if ( this.getResourceBundleDefaultLocale().getLanguage().equalsIgnoreCase( language ) )
382                {
383                    defProperties = p;
384                }
385    
386                fallbackProperties = p;
387    
388                if ( !file.getParentFile().exists() && !file.getParentFile().mkdirs() )
389                {
390                    throw new IOException( getMessage( "failedCreatingDirectory",
391                                                       file.getParentFile().getAbsolutePath() ) );
392    
393                }
394    
395                if ( this.isLoggable( Level.INFO ) )
396                {
397                    this.log( Level.INFO, getMessage( "writing", file.getCanonicalPath() ), null );
398                }
399    
400                OutputStream out = null;
401                try
402                {
403                    out = new FileOutputStream( file );
404                    p.store( out, toolName + ' ' + toolVersion + " - See " + toolUrl );
405                }
406                finally
407                {
408                    if ( out != null )
409                    {
410                        out.close();
411                    }
412                }
413            }
414    
415            if ( defProperties == null )
416            {
417                defProperties = fallbackProperties;
418            }
419    
420            if ( defProperties != null )
421            {
422                final File file = new File( resourcesDirectory, bundlePath + ".properties" );
423                if ( !file.getParentFile().exists() && !file.getParentFile().mkdirs() )
424                {
425                    throw new IOException( getMessage( "failedCreatingDirectory",
426                                                       file.getParentFile().getAbsolutePath() ) );
427    
428                }
429    
430                if ( this.isLoggable( Level.INFO ) )
431                {
432                    this.log( Level.INFO, getMessage( "writing", file.getCanonicalPath() ), null );
433                }
434    
435                OutputStream out = null;
436                try
437                {
438                    out = new FileOutputStream( file );
439                    defProperties.store( out, toolName + ' ' + toolVersion + " - See " + toolUrl );
440                }
441                finally
442                {
443                    if ( out != null )
444                    {
445                        out.close();
446                    }
447                }
448            }
449        }
450    
451        private void assertValidTemplates( final Specification specification )
452        {
453            if ( specification == null )
454            {
455                throw new NullPointerException( "specification" );
456            }
457        }
458    
459        private void assertValidTemplates( final Implementation implementation )
460        {
461            if ( implementation == null )
462            {
463                throw new NullPointerException( "implementation" );
464            }
465    
466            final Messages messages = this.getModules().getMessages( implementation.getIdentifier() );
467    
468            if ( messages != null )
469            {
470                for ( Message m : messages.getMessage() )
471                {
472                    if ( m.getTemplate() != null )
473                    {
474                        for ( Text t : m.getTemplate().getText() )
475                        {
476                            new MessageFormat( t.getValue() );
477                        }
478                    }
479                }
480            }
481        }
482    
483        private static String getMessage( final String key, final Object... arguments )
484        {
485            if ( key == null )
486            {
487                throw new NullPointerException( "key" );
488            }
489    
490            return MessageFormat.format( ResourceBundle.getBundle(
491                ResourceFileProcessor.class.getName().replace( '.', '/' ) ).getString( key ), arguments );
492    
493        }
494    
495    }