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 8641 2012-09-27 06:45:17Z 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    private MavenProject getProject()
120    {
121        return this.project;
122    }
123
124    private File getSourceDirectory()
125    {
126        return this.sourceDirectory;
127    }
128
129    private File getResourceDirectory()
130    {
131        return this.resourceDirectory;
132    }
133
134    private File getBuildDirectory()
135    {
136        return this.buildDirectory;
137    }
138
139    private BundleGenerator getBundleGenerator()
140    {
141        return this.generator;
142    }
143
144    private ModelManager getModelManager()
145    {
146        return this.modelManager;
147    }
148
149    private File getModuleDescriptor()
150    {
151        return this.moduleDescriptor;
152    }
153
154    private String getEncoding()
155    {
156        return this.encoding;
157    }
158
159    private String getDefaultLanguage()
160    {
161        return this.defaultLanguage;
162    }
163
164    public void execute() throws MojoExecutionException, MojoFailureException
165    {
166        if ( !this.getModuleDescriptor().exists() )
167        {
168            throw new MojoExecutionException(
169                this.getMessage( "fileNotFound" ).
170                format( new Object[]
171                {
172                    this.getModuleDescriptor().getAbsolutePath()
173                } ) );
174
175        }
176
177        try
178        {
179            this.assertDirectoryExistence( this.getSourceDirectory() );
180            this.assertDirectoryExistence( this.getResourceDirectory() );
181            this.assertDirectoryExistence( this.getBuildDirectory() );
182
183            this.getProject().addCompileSourceRoot(
184                this.getSourceDirectory().getAbsolutePath() );
185
186            final Resource resource = new Resource();
187            resource.setDirectory( this.getResourceDirectory().
188                getAbsolutePath() );
189
190            resource.setFiltering( false );
191
192            this.getProject().addResource( resource );
193
194            final Module module = this.getModelManager().
195                getModule( this.getModuleDescriptor() );
196
197            if ( module != null )
198            {
199                this.assertValidTemplates( module );
200                if ( module.getImplementations() != null )
201                {
202                    this.generateBundles( module );
203                }
204            }
205        }
206        catch ( Exception e )
207        {
208            throw new MojoExecutionException( e.getMessage(), e );
209        }
210    }
211
212    private MessageFormat getMessage( final String key )
213    {
214        if ( key == null )
215        {
216            throw new NullPointerException( "key" );
217        }
218
219        return new MessageFormat(
220            ResourceBundle.getBundle( JavaResourcesMojo.class.getName() ).
221            getString( key ) );
222
223    }
224
225    private void assertDirectoryExistence( final File directory )
226        throws MojoExecutionException
227    {
228        if ( !directory.exists() && !directory.mkdirs() )
229        {
230            throw new MojoExecutionException(
231                this.getMessage( "cannotCreateDirectory" ).
232                format( new Object[]
233                {
234                    directory.getAbsolutePath()
235                } ) );
236
237
238        }
239    }
240
241    private void generateBundles( final Module module )
242        throws Exception
243    {
244        final Properties bundleHashcodes = new Properties();
245        final File propertiesFile =
246            new File( this.getBuildDirectory(), "bundles.properties" );
247
248        if ( !propertiesFile.exists() )
249        {
250            propertiesFile.createNewFile();
251        }
252
253        final InputStream in = new FileInputStream( propertiesFile );
254        bundleHashcodes.load( in );
255        in.close();
256
257        for ( Iterator it = module.getImplementations().getImplementation().
258            iterator(); it.hasNext(); )
259        {
260            final Implementation impl = (Implementation) it.next();
261            if ( impl.getMessages() == null )
262            {
263                continue;
264            }
265
266            final int bundleHash = this.getModelManager().
267                getHashCode( module, impl );
268
269            final String propertyHash =
270                bundleHashcodes.getProperty( impl.getIdentifier() );
271
272            if ( propertyHash == null ||
273                Integer.valueOf( propertyHash ).intValue() != bundleHash )
274            {
275                bundleHashcodes.setProperty( impl.getIdentifier(),
276                    Integer.toString( bundleHash ) );
277
278                final String bundlePath =
279                    ( this.getModelManager().getJavaPackageName( impl ) +
280                    '.' + this.getModelManager().getJavaTypeName( impl ) ).
281                    replace( '.', File.separatorChar );
282
283                final File bundleFile = new File( this.getSourceDirectory(),
284                    bundlePath + ".java" );
285
286                this.assertDirectoryExistence( bundleFile.getParentFile() );
287
288                final Writer writer =
289                    this.getEncoding() == null
290                    ? new FileWriter( bundleFile )
291                    : new OutputStreamWriter( new FileOutputStream(
292                    bundleFile ),
293                    this.getEncoding() );
294
295                this.getLog().info( this.getMessage( "writingBundle" ).
296                    format( new Object[]
297                    {
298                        bundleFile.getName()
299                    } ) );
300
301                this.getBundleGenerator().generateJava( module, impl, writer );
302
303                writer.close();
304
305                final Map bundleProperties = this.getModelManager().
306                    getBundleProperties( module, impl );
307
308                for ( Iterator properties = bundleProperties.entrySet().
309                    iterator(); properties.hasNext(); )
310                {
311                    final Map.Entry entry = (Map.Entry) properties.next();
312                    final String language = (String) entry.getKey();
313                    final Properties p = (Properties) entry.getValue();
314                    final File file = new File( this.getResourceDirectory(),
315                        bundlePath + "_" + language +
316                        ".properties" );
317
318                    this.getLog().info( this.getMessage( "writingBundle" ).
319                        format( new Object[]
320                        {
321                            file.getName()
322                        } ) );
323
324                    this.assertDirectoryExistence( file.getParentFile() );
325
326                    final OutputStream out = new FileOutputStream( file );
327                    p.store( out, this.getProject().getName() );
328                    out.close();
329
330                    if ( this.getDefaultLanguage().
331                        equalsIgnoreCase( language ) )
332                    {
333                        final File defaultFile =
334                            new File( this.getResourceDirectory(),
335                            bundlePath + ".properties" );
336
337                        this.assertDirectoryExistence(
338                            defaultFile.getParentFile() );
339
340                        this.getLog().info( this.getMessage( "writingBundle" ).
341                            format( new Object[]
342                            {
343                                defaultFile.getName()
344                            } ) );
345
346                        final OutputStream defaultOut =
347                            new FileOutputStream( defaultFile );
348
349                        p.store( defaultOut, this.getProject().getName() );
350                        defaultOut.close();
351                    }
352                }
353            }
354        }
355
356        final OutputStream out = new FileOutputStream( propertiesFile );
357        bundleHashcodes.store( out, this.getClass().getName() + ": " +
358            DateFormat.getDateTimeInstance().
359            format( new Date() ) );
360
361        out.close();
362    }
363
364    private void assertValidTemplates( final Module module )
365        throws MojoExecutionException
366    {
367        if ( module.getImplementations() != null )
368        {
369            for ( Iterator it = module.getImplementations().getImplementation().
370                iterator(); it.hasNext(); )
371            {
372                final Implementation impl = (Implementation) it.next();
373                if ( impl.getMessages() == null )
374                {
375                    continue;
376                }
377
378                for ( Iterator m = impl.getMessages().getMessage().iterator();
379                    m.hasNext(); )
380                {
381                    this.assertValidMessage( (Message) m.next() );
382                }
383            }
384        }
385
386        if ( module.getMessages() != null )
387        {
388            for ( Iterator it = module.getMessages().getMessage().iterator();
389                it.hasNext(); )
390            {
391                this.assertValidMessage( (Message) it.next() );
392            }
393        }
394    }
395
396    private void assertValidMessage( final Message message )
397        throws MojoExecutionException
398    {
399        if ( message.getTemplate() != null )
400        {
401            for ( Iterator it = message.getTemplate().getText().iterator();
402                it.hasNext(); )
403            {
404                final Text text = (Text) it.next();
405                try
406                {
407                    new MessageFormat( text.getValue() );
408                }
409                catch ( IllegalArgumentException e )
410                {
411                    final MessageFormat fmt =
412                        this.getMessage( "illegalTemplate" );
413
414                    throw new MojoExecutionException( fmt.format( new Object[]
415                        {
416                            text.getValue(),
417                            message.getName(),
418                            e.getMessage()
419                        } ), e );
420
421                }
422            }
423        }
424    }
425
426    //-------------------------------------------------------JavaResourcesMojo--
427}