001/*
002 *  jDTAUS Core Resource Mojo
003 *  Copyright (C) 2005 Christian Schulte
004 *  <cs@schulte.it>
005 *
006 *  This library is free software; you can redistribute it and/or
007 *  modify it under the terms of the GNU Lesser General Public
008 *  License as published by the Free Software Foundation; either
009 *  version 2.1 of the License, or any later version.
010 *
011 *  This library is distributed in the hope that it will be useful,
012 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 *  Lesser General Public License for more details.
015 *
016 *  You should have received a copy of the GNU Lesser General Public
017 *  License along with this library; if not, write to the Free Software
018 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
019 *
020 */
021package org.jdtaus.mojo.resource;
022
023import java.io.File;
024import java.io.FileInputStream;
025import java.io.FileOutputStream;
026import java.io.FileWriter;
027import java.io.InputStream;
028import java.io.OutputStream;
029import java.io.OutputStreamWriter;
030import java.io.Writer;
031import java.text.DateFormat;
032import java.text.MessageFormat;
033import java.util.Date;
034import java.util.Iterator;
035import java.util.Map;
036import java.util.Properties;
037import java.util.ResourceBundle;
038import org.apache.maven.model.Resource;
039import org.apache.maven.plugin.AbstractMojo;
040import org.apache.maven.plugin.MojoExecutionException;
041import org.apache.maven.plugin.MojoFailureException;
042import org.apache.maven.project.MavenProject;
043import org.jdtaus.mojo.resource.model.Implementation;
044import org.jdtaus.mojo.resource.model.Message;
045import org.jdtaus.mojo.resource.model.ModelManager;
046import org.jdtaus.mojo.resource.model.Module;
047import org.jdtaus.mojo.resource.model.Text;
048import org.jdtaus.mojo.resource.util.BundleGenerator;
049
050/**
051 * Mojo to generate java resource accessor classes backed by java
052 * <code>ResourceBundle</code>s from a project's module descriptor.
053 *
054 * @goal java-resources
055 * @phase generate-sources
056 * @requiresDependencyResolution compile
057 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
058 * @version $JDTAUS: JavaResourcesMojo.java 8743 2012-10-07 03:06:20Z schulte $
059 */
060public class JavaResourcesMojo extends AbstractMojo
061{
062    //--JavaResourcesMojo-------------------------------------------------------
063
064    /**
065     * Currently executed <code>MavenProject</code>.
066     *
067     * @parameter expression="${project}"
068     * @required
069     */
070    private MavenProject project;
071
072    /**
073     * The directory to generate sources to.
074     *
075     * @parameter expression="${project.build.directory}/generated-sources/java-resources"
076     */
077    private File sourceDirectory;
078
079    /**
080     * The directory to generate resources to.
081     *
082     * @parameter expression="${project.build.directory}/generated-resources/java-resources"
083     */
084    private File resourceDirectory;
085
086    /**
087     * The directory to use for storing hashes for already generated files.
088     *
089     * @parameter expression="${project.build.directory}/java-resources"
090     */
091    private File buildDirectory;
092
093    /**
094     * Project module descriptor to control the mojo.
095     * @parameter expression="${javaResources.moduleDescriptor}"
096     *            default-value="src/main/resources/META-INF/jdtaus/module.xml"
097     */
098    private File moduleDescriptor;
099
100    /**
101     * The encoding to use for writing sources.
102     * @parameter expression="${project.build.sourceEncoding}"
103     */
104    private String encoding;
105
106    /**
107     * The default language for generated bundles.
108     * @parameter expression="${javaResources.defaultLanguage}"
109     *            default-value="en"
110     */
111    private String defaultLanguage;
112
113    /** @component */
114    private BundleGenerator generator;
115
116    /** @component */
117    private ModelManager modelManager;
118
119    /** Creates a new {@code JavaResourcesMojo} instance. */
120    public JavaResourcesMojo()
121    {
122        super();
123    }
124
125    private MavenProject getProject()
126    {
127        return this.project;
128    }
129
130    private File getSourceDirectory()
131    {
132        return this.sourceDirectory;
133    }
134
135    private File getResourceDirectory()
136    {
137        return this.resourceDirectory;
138    }
139
140    private File getBuildDirectory()
141    {
142        return this.buildDirectory;
143    }
144
145    private BundleGenerator getBundleGenerator()
146    {
147        return this.generator;
148    }
149
150    private ModelManager getModelManager()
151    {
152        return this.modelManager;
153    }
154
155    private File getModuleDescriptor()
156    {
157        return this.moduleDescriptor;
158    }
159
160    private String getEncoding()
161    {
162        return this.encoding;
163    }
164
165    private String getDefaultLanguage()
166    {
167        return this.defaultLanguage;
168    }
169
170    public void execute() throws MojoExecutionException, MojoFailureException
171    {
172        if ( !this.getModuleDescriptor().exists() )
173        {
174            throw new MojoExecutionException(
175                this.getMessage( "fileNotFound" ).
176                format( new Object[]
177                {
178                    this.getModuleDescriptor().getAbsolutePath()
179                } ) );
180
181        }
182
183        try
184        {
185            this.assertDirectoryExistence( this.getSourceDirectory() );
186            this.assertDirectoryExistence( this.getResourceDirectory() );
187            this.assertDirectoryExistence( this.getBuildDirectory() );
188
189            this.getProject().addCompileSourceRoot(
190                this.getSourceDirectory().getAbsolutePath() );
191
192            final Resource resource = new Resource();
193            resource.setDirectory( this.getResourceDirectory().
194                getAbsolutePath() );
195
196            resource.setFiltering( false );
197
198            this.getProject().addResource( resource );
199
200            final Module module = this.getModelManager().
201                getModule( this.getModuleDescriptor() );
202
203            if ( module != null )
204            {
205                this.assertValidTemplates( module );
206                if ( module.getImplementations() != null )
207                {
208                    this.generateBundles( module );
209                }
210            }
211        }
212        catch ( final Exception e )
213        {
214            throw new MojoExecutionException( e.getMessage(), e );
215        }
216    }
217
218    private MessageFormat getMessage( final String key )
219    {
220        if ( key == null )
221        {
222            throw new NullPointerException( "key" );
223        }
224
225        return new MessageFormat(
226            ResourceBundle.getBundle( JavaResourcesMojo.class.getName() ).
227            getString( key ) );
228
229    }
230
231    private void assertDirectoryExistence( final File directory )
232        throws MojoExecutionException
233    {
234        if ( !directory.exists() && !directory.mkdirs() )
235        {
236            throw new MojoExecutionException(
237                this.getMessage( "cannotCreateDirectory" ).
238                format( new Object[]
239                {
240                    directory.getAbsolutePath()
241                } ) );
242
243
244        }
245    }
246
247    private void generateBundles( final Module module )
248        throws Exception
249    {
250        InputStream in = null;
251        OutputStream out = null;
252        Writer writer = null;
253
254        try
255        {
256            final Properties bundleHashcodes = new Properties();
257            final File propertiesFile =
258                new File( this.getBuildDirectory(), "bundles.properties" );
259
260            if ( !propertiesFile.exists() && !propertiesFile.createNewFile() )
261            {
262                final MessageFormat fmt =
263                    this.getMessage( "cannotCreateFile" );
264
265                throw new MojoExecutionException( fmt.format( new Object[]
266                    {
267                        propertiesFile.getAbsolutePath()
268                    } ) );
269
270            }
271
272            in = new FileInputStream( propertiesFile );
273            bundleHashcodes.load( in );
274            in.close();
275            in = null;
276
277            for ( final Iterator it = module.getImplementations().
278                getImplementation().iterator(); it.hasNext(); )
279            {
280                final Implementation impl = (Implementation) it.next();
281                if ( impl.getMessages() == null )
282                {
283                    continue;
284                }
285
286                final int bundleHash =
287                    this.getModelManager().getHashCode( module, impl );
288
289                final String propertyHash =
290                    bundleHashcodes.getProperty( impl.getIdentifier() );
291
292                if ( propertyHash == null || Integer.valueOf( propertyHash ).
293                    intValue() != bundleHash )
294                {
295                    bundleHashcodes.setProperty(
296                        impl.getIdentifier(), Integer.toString( bundleHash ) );
297
298                    final String bundlePath =
299                        ( this.getModelManager().getJavaPackageName( impl )
300                          + '.' + this.getModelManager().getJavaTypeName( impl ) ).
301                        replace( '.', File.separatorChar );
302
303                    final File bundleFile = new File( this.getSourceDirectory(),
304                                                      bundlePath + ".java" );
305
306                    this.assertDirectoryExistence( bundleFile.getParentFile() );
307
308                    writer =
309                        this.getEncoding() == null
310                        ? new FileWriter( bundleFile )
311                        : new OutputStreamWriter( new FileOutputStream(
312                        bundleFile ), this.getEncoding() );
313
314                    this.getLog().info( this.getMessage( "writingBundle" ).
315                        format( new Object[]
316                        {
317                            bundleFile.getName()
318                        } ) );
319
320                    this.getBundleGenerator().
321                        generateJava( module, impl, writer );
322
323                    writer.close();
324                    writer = null;
325
326                    final Map bundleProperties =
327                        this.getModelManager().
328                        getBundleProperties( module, impl );
329
330                    for ( final Iterator it2 = bundleProperties.entrySet().
331                        iterator(); it2.hasNext(); )
332                    {
333                        final Map.Entry entry = (Map.Entry) it2.next();
334                        final String language = (String) entry.getKey();
335                        final Properties p = (Properties) entry.getValue();
336                        final File file = new File( this.getResourceDirectory(),
337                                                    bundlePath + "_" + language
338                                                    + ".properties" );
339
340                        this.getLog().info( this.getMessage( "writingBundle" ).
341                            format( new Object[]
342                            {
343                                file.getName()
344                            } ) );
345
346                        this.assertDirectoryExistence( file.getParentFile() );
347
348                        out = new FileOutputStream( file );
349                        p.store( out, this.getProject().getName() );
350                        out.close();
351                        out = null;
352
353                        if ( this.getDefaultLanguage().
354                            equalsIgnoreCase( language ) )
355                        {
356                            final File defaultFile =
357                                new File( this.getResourceDirectory(),
358                                          bundlePath + ".properties" );
359
360                            this.assertDirectoryExistence(
361                                defaultFile.getParentFile() );
362
363                            this.getLog().info( this.
364                                getMessage( "writingBundle" ).
365                                format( new Object[]
366                                {
367                                    defaultFile.getName()
368                                } ) );
369
370                            out = new FileOutputStream( defaultFile );
371                            p.store( out, this.getProject().getName() );
372                            out.close();
373                            out = null;
374                        }
375                    }
376                }
377            }
378
379            out = new FileOutputStream( propertiesFile );
380            bundleHashcodes.store( out, this.getClass().getName() + ": "
381                                        + DateFormat.getDateTimeInstance().
382                format( new Date() ) );
383
384            out.close();
385            out = null;
386        }
387        finally
388        {
389            try
390            {
391                if ( in != null )
392                {
393                    in.close();
394                }
395            }
396            finally
397            {
398                try
399                {
400                    if ( out != null )
401                    {
402                        out.close();
403                    }
404                }
405                finally
406                {
407                    if ( writer != null )
408                    {
409                        writer.close();
410                    }
411                }
412            }
413        }
414    }
415
416    private void assertValidTemplates( final Module module )
417        throws MojoExecutionException
418    {
419        if ( module.getImplementations() != null )
420        {
421            for ( final Iterator it = module.getImplementations().
422                getImplementation().iterator(); it.hasNext(); )
423            {
424                final Implementation impl = (Implementation) it.next();
425                if ( impl.getMessages() == null )
426                {
427                    continue;
428                }
429
430                for ( final Iterator m = impl.getMessages().getMessage().
431                    iterator(); m.hasNext(); )
432                {
433                    this.assertValidMessage( (Message) m.next() );
434                }
435            }
436        }
437
438        if ( module.getMessages() != null )
439        {
440            for ( final Iterator it = module.getMessages().getMessage().
441                iterator(); it.hasNext(); )
442            {
443                this.assertValidMessage( (Message) it.next() );
444            }
445        }
446    }
447
448    private void assertValidMessage( final Message message )
449        throws MojoExecutionException
450    {
451        if ( message.getTemplate() != null )
452        {
453            for ( final Iterator it = message.getTemplate().getText().
454                iterator(); it.hasNext(); )
455            {
456                final Text text = (Text) it.next();
457                try
458                {
459                    new MessageFormat( text.getValue() );
460                }
461                catch ( final IllegalArgumentException e )
462                {
463                    final MessageFormat fmt =
464                        this.getMessage( "illegalTemplate" );
465
466                    throw new MojoExecutionException( fmt.format( new Object[]
467                        {
468                            text.getValue(),
469                            message.getName(),
470                            e.getMessage()
471                        } ), e );
472
473                }
474            }
475        }
476    }
477
478    //-------------------------------------------------------JavaResourcesMojo--
479}