001/*
002 *   Copyright (C) Christian Schulte, 2005-206
003 *   All rights reserved.
004 *
005 *   Redistribution and use in source and binary forms, with or without
006 *   modification, are permitted provided that the following conditions
007 *   are met:
008 *
009 *     o Redistributions of source code must retain the above copyright
010 *       notice, this list of conditions and the following disclaimer.
011 *
012 *     o Redistributions in binary form must reproduce the above copyright
013 *       notice, this list of conditions and the following disclaimer in
014 *       the documentation and/or other materials provided with the
015 *       distribution.
016 *
017 *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
018 *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
019 *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
020 *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
021 *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
022 *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
023 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
024 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
026 *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027 *
028 *   $JOMC: AbstractJomcMojo.java 4555 2012-05-24 23:35:41Z schulte2005 $
029 *
030 */
031package org.jomc.mojo;
032
033import java.io.BufferedReader;
034import java.io.File;
035import java.io.IOException;
036import java.io.InputStream;
037import java.io.StringReader;
038import java.io.StringWriter;
039import java.net.MalformedURLException;
040import java.net.SocketTimeoutException;
041import java.net.URI;
042import java.net.URISyntaxException;
043import java.net.URL;
044import java.net.URLClassLoader;
045import java.net.URLConnection;
046import java.util.Collection;
047import java.util.Date;
048import java.util.HashSet;
049import java.util.Iterator;
050import java.util.List;
051import java.util.Locale;
052import java.util.Map;
053import java.util.Properties;
054import java.util.Set;
055import java.util.logging.Level;
056import javax.xml.bind.JAXBException;
057import javax.xml.bind.Marshaller;
058import javax.xml.transform.ErrorListener;
059import javax.xml.transform.Transformer;
060import javax.xml.transform.TransformerConfigurationException;
061import javax.xml.transform.TransformerException;
062import javax.xml.transform.TransformerFactory;
063import javax.xml.transform.stream.StreamSource;
064import org.apache.commons.lang.StringEscapeUtils;
065import org.apache.commons.lang.StringUtils;
066import org.apache.maven.artifact.Artifact;
067import org.apache.maven.artifact.ArtifactUtils;
068import org.apache.maven.execution.MavenSession;
069import org.apache.maven.plugin.AbstractMojo;
070import org.apache.maven.plugin.MojoExecutionException;
071import org.apache.maven.plugin.MojoFailureException;
072import org.apache.maven.plugin.descriptor.MojoDescriptor;
073import org.apache.maven.project.MavenProject;
074import org.jomc.model.Module;
075import org.jomc.model.Modules;
076import org.jomc.model.modlet.DefaultModelProcessor;
077import org.jomc.model.modlet.DefaultModelProvider;
078import org.jomc.model.modlet.ModelHelper;
079import org.jomc.modlet.DefaultModelContext;
080import org.jomc.modlet.DefaultModletProvider;
081import org.jomc.modlet.Model;
082import org.jomc.modlet.ModelContext;
083import org.jomc.modlet.ModelContextFactory;
084import org.jomc.modlet.ModelException;
085import org.jomc.modlet.ModelValidationReport;
086import org.jomc.tools.ClassFileProcessor;
087import org.jomc.tools.JomcTool;
088import org.jomc.tools.ResourceFileProcessor;
089import org.jomc.tools.SourceFileProcessor;
090import org.jomc.tools.modlet.ToolsModelProcessor;
091import org.jomc.tools.modlet.ToolsModelProvider;
092
093/**
094 * Base class for executing {@code JomcTool}s.
095 *
096 * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
097 * @version $JOMC: AbstractJomcMojo.java 4555 2012-05-24 23:35:41Z schulte2005 $
098 */
099public abstract class AbstractJomcMojo extends AbstractMojo
100{
101
102    /**
103     * The encoding to use for reading and writing files.
104     *
105     * @parameter default-value="${project.build.sourceEncoding}" expression="${jomc.sourceEncoding}"
106     */
107    private String sourceEncoding;
108
109    /**
110     * The encoding to use for reading templates.
111     * <p><strong>Deprecated:</strong> As of JOMC 1.3, please use the 'defaultTemplateEncoding' parameter. This
112     * parameter will be removed in version 2.0.</p>
113     *
114     * @parameter expression="${jomc.templateEncoding}"
115     */
116    @Deprecated
117    private String templateEncoding;
118
119    /**
120     * The encoding to use for reading templates.
121     *
122     * @parameter expression="${jomc.defaultTemplateEncoding}"
123     *
124     * @since 1.3
125     */
126    private String defaultTemplateEncoding;
127
128    /**
129     * Location to search for templates in addition to searching the class path of the plugin.
130     * <p>First an attempt is made to parse the location value to an URL. On successful parsing, that URL is used.
131     * Otherwise the location value is interpreted as a directory name relative to the base directory of the project.
132     * If that directory exists, that directory is used. If nothing is found at the given location, a warning message is
133     * logged.</p>
134     *
135     * @parameter expression="${jomc.templateLocation}"
136     * @since 1.2
137     */
138    private String templateLocation;
139
140    /**
141     * The template profile to use when accessing templates.
142     *
143     * @parameter expression="${jomc.templateProfile}"
144     */
145    private String templateProfile;
146
147    /**
148     * The default template profile to use when accessing templates.
149     *
150     * @parameter expression="${jomc.defaultTemplateProfile}"
151     */
152    private String defaultTemplateProfile;
153
154    /**
155     * The location to search for providers.
156     *
157     * @parameter expression="${jomc.providerLocation}"
158     */
159    private String providerLocation;
160
161    /**
162     * The location to search for platform providers.
163     *
164     * @parameter expression="${jomc.platformProviderLocation}"
165     */
166    private String platformProviderLocation;
167
168    /**
169     * The identifier of the model to process.
170     *
171     * @parameter default-value="http://jomc.org/model" expression="${jomc.model}"
172     */
173    private String model;
174
175    /**
176     * The name of the {@code ModelContextFactory} implementation class backing the task.
177     *
178     * @parameter expression="${jomc.modelContextFactoryClassName}"
179     * @since 1.2
180     */
181    private String modelContextFactoryClassName;
182
183    /**
184     * The location to search for modlets.
185     *
186     * @parameter expression="${jomc.modletLocation}"
187     */
188    private String modletLocation;
189
190    /**
191     * The {@code http://jomc.org/modlet} namespace schema system id.
192     *
193     * @parameter expression="${jomc.modletSchemaSystemId}"
194     * @since 1.2
195     */
196    private String modletSchemaSystemId;
197
198    /**
199     * The location to search for modules.
200     *
201     * @parameter expression="${jomc.moduleLocation}"
202     */
203    private String moduleLocation;
204
205    /**
206     * The location to search for transformers.
207     *
208     * @parameter expression="${jomc.transformerLocation}"
209     */
210    private String transformerLocation;
211
212    /**
213     * The indentation string ('\t' for tab).
214     *
215     * @parameter expression="${jomc.indentation}"
216     */
217    private String indentation;
218
219    /**
220     * The line separator ('\r\n' for DOS, '\r' for Mac, '\n' for Unix).
221     *
222     * @parameter expression="${jomc.lineSeparator}"
223     */
224    private String lineSeparator;
225
226    /**
227     * The locale.
228     * <pre>
229     * &lt;locale>
230     *   &lt;language>Lowercase two-letter ISO-639 code.&lt;/language>
231     *   &lt;country>Uppercase two-letter ISO-3166 code.&lt;/country>
232     *   &lt;variant>Vendor and browser specific code.&lt;/variant>
233     * &lt;/locale>
234     * </pre>
235     *
236     * @parameter
237     * @since 1.2
238     * @see Locale
239     */
240    private LocaleType locale;
241
242    /**
243     * Controls verbosity of the plugin.
244     *
245     * @parameter expression="${jomc.verbose}" default-value="false"
246     */
247    private boolean verbose;
248
249    /**
250     * Controls processing of source code files.
251     *
252     * @parameter expression="${jomc.sourceProcessing}" default-value="true"
253     */
254    private boolean sourceProcessingEnabled;
255
256    /**
257     * Controls processing of resource files.
258     *
259     * @parameter expression="${jomc.resourceProcessing}" default-value="true"
260     */
261    private boolean resourceProcessingEnabled;
262
263    /**
264     * Controls processing of class files.
265     *
266     * @parameter expression="${jomc.classProcessing}" default-value="true"
267     */
268    private boolean classProcessingEnabled;
269
270    /**
271     * Controls processing of models.
272     *
273     * @parameter expression="${jomc.modelProcessing}" default-value="true"
274     */
275    private boolean modelProcessingEnabled;
276
277    /**
278     * Controls model object class path resolution.
279     *
280     * @parameter expression="${jomc.modelObjectClasspathResolution}" default-value="true"
281     */
282    private boolean modelObjectClasspathResolutionEnabled;
283
284    /**
285     * Name of the module to process.
286     *
287     * @parameter default-value="${project.name}" expression="${jomc.moduleName}"
288     */
289    private String moduleName;
290
291    /**
292     * Name of the test module to process.
293     *
294     * @parameter default-value="${project.name} Tests" expression="${jomc.testModuleName}"
295     */
296    private String testModuleName;
297
298    /**
299     * Directory holding the compiled class files of the project.
300     * <p><strong>Deprecated:</strong> As of JOMC 1.1, please use the 'outputDirectory' parameter. This parameter will
301     * be removed in version 2.0.</p>
302     *
303     * @parameter
304     */
305    @Deprecated
306    private String classesDirectory;
307
308    /**
309     * Directory holding the compiled test class files of the project.
310     * <p><strong>Deprecated:</strong> As of JOMC 1.1, please use the 'testOutputDirectory' parameter. This parameter
311     * will be removed in version 2.0.</p>
312     *
313     * @parameter
314     */
315    @Deprecated
316    private String testClassesDirectory;
317
318    /**
319     * Output directory of the project.
320     *
321     * @parameter default-value="${project.build.outputDirectory}" expression="${jomc.outputDirectory}"
322     * @since 1.1
323     */
324    private String outputDirectory;
325
326    /**
327     * Test output directory of the project.
328     *
329     * @parameter default-value="${project.build.testOutputDirectory}" expression="${jomc.testOutputDirectory}"
330     * @since 1.1
331     */
332    private String testOutputDirectory;
333
334    /**
335     * Directory holding the source files of the project.
336     *
337     * @parameter default-value="${project.build.sourceDirectory}" expression="${jomc.sourceDirectory}"
338     * @since 1.1
339     */
340    private String sourceDirectory;
341
342    /**
343     * Directory holding the test source files of the project.
344     *
345     * @parameter default-value="${project.build.testSourceDirectory}" expression="${jomc.testSourceDirectory}"
346     * @since 1.1
347     */
348    private String testSourceDirectory;
349
350    /**
351     * Directory holding the session related files of the project.
352     *
353     * @parameter default-value="${project.build.directory}/jomc-sessions" expression="${jomc.sessionDirectory}"
354     * @since 1.1
355     */
356    private String sessionDirectory;
357
358    /**
359     * Directory holding the reports of the project.
360     *
361     * @parameter default-value="${project.reporting.outputDirectory}" expression="${jomc.reportOutputDirectory}"
362     * @since 1.1
363     */
364    private String reportOutputDirectory;
365
366    /**
367     * Velocity runtime properties.
368     * <pre>
369     * &lt;velocityProperties>
370     *   &lt;velocityProperty>
371     *     &lt;key>The name of the property.&lt;/key>
372     *     &lt;value>The value of the property.&lt;/value>
373     *     &lt;type>The name of the class of the properties object.&lt;/type>
374     *   &lt;/velocityProperty>
375     * &lt;/velocityProperties>
376     * </pre>
377     *
378     * @parameter
379     * @since 1.2
380     */
381    private List<VelocityProperty> velocityProperties;
382
383    /**
384     * Velocity runtime property resources.
385     * <pre>
386     * &lt;velocityPropertyResources>
387     *   &lt;velocityPropertyResource>
388     *     &lt;location>The location of the properties resource.&lt;/location>
389     *     &lt;optional>Flag indicating the properties resource is optional.&lt;/optional>
390     *     &lt;format>The format of the properties resource.&lt;/format>
391     *     &lt;connectTimeout>Timeout value, in milliseconds.&lt;/connectTimeout>
392     *     &lt;readTimeout>Timeout value, in milliseconds.&lt;/readTimeout>
393     *   &lt;/velocityPropertyResource>
394     * &lt;/velocityPropertyResources>
395     * </pre>
396     * <p>The location value is used to first search the class path of the plugin. If a class path resource is found,
397     * that resource is used. If no class path resource is found, an attempt is made to parse the location value to an
398     * URL. On successful parsing, that URL is used. Otherwise the location value is interpreted as a file name relative
399     * to the base directory of the project. If that file exists, that file is used. If nothing is found at the given
400     * location, depending on the optional flag, a warning message is logged or a build failure is produced.</p>
401     * <p>The optional flag is used to flag the resource optional. When an optional resource is not found, a warning
402     * message is logged instead of producing a build failure.<br/><b>Default value is:</b> false</p>
403     * <p>The format value is used to specify the format of the properties resource. Supported values are {@code plain}
404     * and {@code xml}.<br/><b>Default value is:</b> plain</p>
405     * <p>The connectTimeout value is used to specify the timeout, in milliseconds, to be used when opening
406     * communications links to the resource. A timeout of zero is interpreted as an infinite timeout.<br/>
407     * <b>Default value is:</b> 60000</p>
408     * <p>The readTimeout value is used to specify the timeout, in milliseconds, to be used when reading the resource.
409     * A timeout of zero is interpreted as an infinite timeout.<br/>
410     * <b>Default value is:</b> 60000</p>
411     *
412     * @parameter
413     * @since 1.2
414     */
415    private List<VelocityPropertyResource> velocityPropertyResources;
416
417    /**
418     * Template parameters.
419     * <pre>
420     * &lt;templateParameters>
421     *   &lt;templateParameter>
422     *     &lt;key>The name of the parameter.&lt;/key>
423     *     &lt;value>The value of the parameter.&lt;/value>
424     *     &lt;type>The name of the class of the parameter's object.&lt;/type>
425     *   &lt;/templateParameter>
426     * &lt;/templateParameters>
427     * </pre>
428     *
429     * @parameter
430     * @since 1.2
431     */
432    private List<TemplateParameter> templateParameters;
433
434    /**
435     * Template parameter resources.
436     * <pre>
437     * &lt;templateParameterResources>
438     *   &lt;templateParameterResource>
439     *     &lt;location>The location of the properties resource.&lt;/location>
440     *     &lt;optional>Flag indicating the properties resource is optional.&lt;/optional>
441     *     &lt;format>The format of the properties resource.&lt;/format>
442     *     &lt;connectTimeout>Timeout value, in milliseconds.&lt;/connectTimeout>
443     *     &lt;readTimeout>Timeout value, in milliseconds.&lt;/readTimeout>
444     *   &lt;/templateParameterResource>
445     * &lt;/templateParameterResources>
446     * </pre>
447     * <p>The location value is used to first search the class path of the plugin. If a class path resource is found,
448     * that resource is used. If no class path resource is found, an attempt is made to parse the location value to an
449     * URL. On successful parsing, that URL is used. Otherwise the location value is interpreted as a file name relative
450     * to the base directory of the project. If that file exists, that file is used. If nothing is found at the given
451     * location, depending on the optional flag, a warning message is logged or a build failure is produced.</p>
452     * <p>The optional flag is used to flag the resource optional. When an optional resource is not found, a warning
453     * message is logged instead of producing a build failure.<br/><b>Default value is:</b> false</p>
454     * <p>The format value is used to specify the format of the properties resource. Supported values are {@code plain}
455     * and {@code xml}.<br/><b>Default value is:</b> plain</p>
456     * <p>The connectTimeout value is used to specify the timeout, in milliseconds, to be used when opening
457     * communications links to the resource. A timeout of zero is interpreted as an infinite timeout.<br/>
458     * <b>Default value is:</b> 60000</p>
459     * <p>The readTimeout value is used to specify the timeout, in milliseconds, to be used when reading the resource.
460     * A timeout of zero is interpreted as an infinite timeout.<br/>
461     * <b>Default value is:</b> 60000</p>
462     *
463     * @parameter
464     * @since 1.2
465     */
466    private List<TemplateParameterResource> templateParameterResources;
467
468    /**
469     * Global transformation parameters.
470     * <pre>
471     * &lt;transformationParameters>
472     *   &lt;transformationParameter>
473     *     &lt;key>The name of the parameter.&lt;/key>
474     *     &lt;value>The value of the parameter.&lt;/value>
475     *     &lt;type>The name of the class of the parameter's object.&lt;/type>
476     *   &lt;/transformationParameter>
477     * &lt;/transformationParameters>
478     * </pre>
479     *
480     * @parameter
481     * @since 1.2
482     */
483    private List<TransformationParameter> transformationParameters;
484
485    /**
486     * Global transformation output properties.
487     * <pre>
488     * &lt;transformationOutputProperties>
489     *   &lt;transformationOutputProperty>
490     *     &lt;key>The name of the property.&lt;/key>
491     *     &lt;value>The value of the property.&lt;/value>
492     *     &lt;type>The name of the class of the properties object.&lt;/type>
493     *   &lt;/transformationOutputProperty>
494     * &lt;/transformationOutputProperties>
495     * </pre>
496     *
497     * @parameter
498     * @since 1.2
499     */
500    private List<TransformationOutputProperty> transformationOutputProperties;
501
502    /**
503     * Global transformation parameter resources.
504     * <pre>
505     * &lt;transformationParameterResources>
506     *   &lt;transformationParameterResource>
507     *     &lt;location>The location of the properties resource.&lt;/location>
508     *     &lt;optional>Flag indicating the properties resource is optional.&lt;/optional>
509     *     &lt;format>The format of the properties resource.&lt;/format>
510     *     &lt;connectTimeout>Timeout value, in milliseconds.&lt;/connectTimeout>
511     *     &lt;readTimeout>Timeout value, in milliseconds.&lt;/readTimeout>
512     *   &lt;/transformationParameterResource>
513     * &lt;/transformationParameterResources>
514     * </pre>
515     * <p>The location value is used to first search the class path of the plugin. If a class path resource is found,
516     * that resource is used. If no class path resource is found, an attempt is made to parse the location value to an
517     * URL. On successful parsing, that URL is used. Otherwise the location value is interpreted as a file name relative
518     * to the base directory of the project. If that file exists, that file is used. If nothing is found at the given
519     * location, depending on the optional flag, a warning message is logged or a build failure is produced.</p>
520     * <p>The optional flag is used to flag the resource optional. When an optional resource is not found, a warning
521     * message is logged instead of producing a build failure.<br/><b>Default value is:</b> false</p>
522     * <p>The format value is used to specify the format of the properties resource. Supported values are {@code plain}
523     * and {@code xml}.<br/><b>Default value is:</b> plain</p>
524     * <p>The connectTimeout value is used to specify the timeout, in milliseconds, to be used when opening
525     * communications links to the resource. A timeout of zero is interpreted as an infinite timeout.<br/>
526     * <b>Default value is:</b> 60000</p>
527     * <p>The readTimeout value is used to specify the timeout, in milliseconds, to be used when reading the resource.
528     * A timeout of zero is interpreted as an infinite timeout.<br/>
529     * <b>Default value is:</b> 60000</p>
530     *
531     * @parameter
532     * @since 1.2
533     */
534    private List<TransformationParameterResource> transformationParameterResources;
535
536    /**
537     * Class name of the {@code ClassFileProcessor} backing the goal.
538     *
539     * @parameter default-value="org.jomc.tools.ClassFileProcessor" expression="${jomc.classFileProcessorClassName}"
540     * @since 1.2
541     */
542    private String classFileProcessorClassName;
543
544    /**
545     * Class name of the {@code ResourceFileProcessor} backing the goal.
546     *
547     * @parameter default-value="org.jomc.tools.ResourceFileProcessor"
548     *            expression="${jomc.resourceFileProcessorClassName}"
549     * @since 1.2
550     */
551    private String resourceFileProcessorClassName;
552
553    /**
554     * Class name of the {@code SourceFileProcessor} backing the goal.
555     *
556     * @parameter default-value="org.jomc.tools.SourceFileProcessor" expression="${jomc.sourceFileProcessorClassName}"
557     * @since 1.2
558     */
559    private String sourceFileProcessorClassName;
560
561    /**
562     * {@code ModelContext} attributes.
563     * <pre>
564     * &lt;modelContextAttributes>
565     *   &lt;modelContextAttribute>
566     *     &lt;key>The name of the attribute.&lt;/key>
567     *     &lt;value>The value of the attribute.&lt;/value>
568     *     &lt;type>The name of the class of the attributes's object.&lt;/type>
569     *   &lt;/modelContextAttribute>
570     * &lt;/modelContextAttributes>
571     * </pre>
572     *
573     * @parameter
574     * @since 1.2
575     */
576    private List<ModelContextAttribute> modelContextAttributes;
577
578    /**
579     * Flag controlling JAXP schema validation of model resources.
580     *
581     * @parameter default-value="true" expression="${jomc.modelResourceValidationEnabled}"
582     *
583     * @since 1.2
584     */
585    private boolean modelResourceValidationEnabled;
586
587    /**
588     * Flag controlling JAXP schema validation of modlet resources.
589     *
590     * @parameter default-value="true" expression="${jomc.modletResourceValidationEnabled}"
591     *
592     * @since 1.2
593     */
594    private boolean modletResourceValidationEnabled;
595
596    /**
597     * The Maven project of the instance.
598     *
599     * @parameter expression="${project}"
600     * @required
601     * @readonly
602     */
603    private MavenProject mavenProject;
604
605    /**
606     * List of plugin artifacts.
607     *
608     * @parameter expression="${plugin.artifacts}"
609     * @required
610     * @readonly
611     */
612    private List<Artifact> pluginArtifacts;
613
614    /**
615     * The Maven session of the instance.
616     *
617     * @parameter expression="${session}"
618     * @required
619     * @readonly
620     * @since 1.1
621     */
622    private MavenSession mavenSession;
623
624    /** Creates a new {@code AbstractJomcMojo} instance. */
625    public AbstractJomcMojo()
626    {
627        super();
628    }
629
630    /**
631     * {@inheritDoc}
632     * @see #assertValidParameters()
633     * @see #isExecutionPermitted()
634     * @see #executeTool()
635     */
636    public void execute() throws MojoExecutionException, MojoFailureException
637    {
638        this.assertValidParameters();
639
640        try
641        {
642            this.logSeparator();
643
644            if ( this.isLoggable( Level.INFO ) )
645            {
646                this.log( Level.INFO, Messages.getMessage( "title" ), null );
647            }
648
649            if ( this.isExecutionPermitted() )
650            {
651                this.executeTool();
652            }
653            else if ( this.isLoggable( Level.INFO ) )
654            {
655                this.log( Level.INFO, Messages.getMessage( "executionSuppressed", this.getExecutionStrategy() ), null );
656            }
657        }
658        catch ( final Exception e )
659        {
660            throw new MojoExecutionException( Messages.getMessage( e ), e );
661        }
662        finally
663        {
664            JomcTool.setDefaultTemplateProfile( null );
665            this.logSeparator();
666        }
667    }
668
669    /**
670     * Validates the parameters of the goal.
671     *
672     * @throws MojoFailureException if illegal parameter values are detected.
673     *
674     * @see #assertValidResources(java.util.Collection)
675     * @since 1.2
676     */
677    protected void assertValidParameters() throws MojoFailureException
678    {
679        this.assertValidResources( this.templateParameterResources );
680        this.assertValidResources( this.transformationParameterResources );
681        this.assertValidResources( this.velocityPropertyResources );
682    }
683
684    /**
685     * Validates a given resource collection.
686     *
687     * @param resources The resource collection to validate or {@code null}.
688     *
689     * @throws MojoFailureException if a location property of a given resource holds a {@code null} value or a given
690     * {@code PropertiesResourceType} holds an illegal format.
691     *
692     * @see #assertValidParameters()
693     * @see PropertiesResourceType#isFormatSupported(java.lang.String)
694     * @since 1.2
695     */
696    protected final void assertValidResources( final Collection<? extends ResourceType> resources )
697        throws MojoFailureException
698    {
699        if ( resources != null )
700        {
701            for ( ResourceType r : resources )
702            {
703                if ( r.getLocation() == null )
704                {
705                    throw new MojoFailureException( Messages.getMessage( "mandatoryParameter", "location" ) );
706                }
707
708                if ( r instanceof PropertiesResourceType )
709                {
710                    final PropertiesResourceType p = (PropertiesResourceType) r;
711
712                    if ( !PropertiesResourceType.isFormatSupported( p.getFormat() ) )
713                    {
714                        throw new MojoFailureException( Messages.getMessage(
715                            "illegalPropertiesFormat", p.getFormat(),
716                            StringUtils.join( PropertiesResourceType.getSupportedFormats(), ',' ) ) );
717
718                    }
719                }
720            }
721        }
722    }
723
724    /**
725     * Executes this tool.
726     *
727     * @throws Exception if execution of this tool fails.
728     */
729    protected abstract void executeTool() throws Exception;
730
731    /**
732     * Gets the goal of the instance.
733     *
734     * @return The goal of the instance.
735     *
736     * @throws MojoExecutionException if getting the goal of the instance fails.
737     * @since 1.1
738     */
739    protected abstract String getGoal() throws MojoExecutionException;
740
741    /**
742     * Gets the execution strategy of the instance.
743     *
744     * @return The execution strategy of the instance.
745     *
746     * @throws MojoExecutionException if getting the execution strategy of the instance fails.
747     * @since 1.1
748     */
749    protected abstract String getExecutionStrategy() throws MojoExecutionException;
750
751    /**
752     * Gets a flag indicating the current execution is permitted.
753     *
754     * @return {@code true}, if the current execution is permitted; {@code false}, if the current execution is
755     * suppressed.
756     *
757     * @throws MojoExecutionException if getting the flag fails.
758     *
759     * @since 1.1
760     * @see #getGoal()
761     * @see #getExecutionStrategy()
762     */
763    protected boolean isExecutionPermitted() throws MojoExecutionException
764    {
765        try
766        {
767            boolean permitted = true;
768
769            if ( MojoDescriptor.SINGLE_PASS_EXEC_STRATEGY.equals( this.getExecutionStrategy() ) )
770            {
771                final File flagFile =
772                    new File( this.getSessionDirectory(),
773                              ArtifactUtils.versionlessKey( this.getMavenProject().getArtifact() ).hashCode()
774                              + "-" + this.getGoal()
775                              + "-" + this.getMavenSession().getStartTime().getTime() + ".flg" );
776
777                if ( !this.getSessionDirectory().exists() && !this.getSessionDirectory().mkdirs() )
778                {
779                    throw new MojoExecutionException( Messages.getMessage(
780                        "failedCreatingDirectory", this.getSessionDirectory().getAbsolutePath() ) );
781
782                }
783
784                permitted = flagFile.createNewFile();
785            }
786
787            return permitted;
788        }
789        catch ( final IOException e )
790        {
791            throw new MojoExecutionException( Messages.getMessage( e ), e );
792        }
793    }
794
795    /**
796     * Gets the Maven project of the instance.
797     *
798     * @return The Maven project of the instance.
799     *
800     * @throws MojoExecutionException if getting the Maven project of the instance fails.
801     */
802    protected MavenProject getMavenProject() throws MojoExecutionException
803    {
804        return this.mavenProject;
805    }
806
807    /**
808     * Gets the Maven session of the instance.
809     *
810     * @return The Maven session of the instance.
811     *
812     * @throws MojoExecutionException if getting the Maven session of the instance fails.
813     *
814     * @since 1.1
815     */
816    protected MavenSession getMavenSession() throws MojoExecutionException
817    {
818        return this.mavenSession;
819    }
820
821    /**
822     * Gets an absolute {@code File} instance for a given name.
823     * <p>This method constructs a new {@code File} instance using the given name. If the resulting file is not
824     * absolute, the value of the {@code basedir} property of the current Maven project is prepended.</p>
825     *
826     * @param name The name to get an absolute {@code File} instance for.
827     *
828     * @return An absolute {@code File} instance constructed from {@code name}.
829     *
830     * @throws MojoExecutionException if getting an absolute {@code File} instance for {@code name} fails.
831     * @throws NullPointerException if {@code name} is {@code null}.
832     *
833     * @since 1.1
834     */
835    protected File getAbsoluteFile( final String name ) throws MojoExecutionException
836    {
837        if ( name == null )
838        {
839            throw new NullPointerException( "name" );
840        }
841
842        File file = new File( name );
843        if ( !file.isAbsolute() )
844        {
845            file = new File( this.getMavenProject().getBasedir(), name );
846        }
847
848        return file;
849    }
850
851    /**
852     * Gets the directory holding the compiled class files of the project.
853     *
854     * @return The directory holding the compiled class files of the project.
855     *
856     * @throws MojoExecutionException if getting the directory fails.
857     *
858     * @since 1.1
859     */
860    protected File getOutputDirectory() throws MojoExecutionException
861    {
862        if ( this.classesDirectory != null )
863        {
864            if ( this.isLoggable( Level.WARNING ) )
865            {
866                this.log( Level.WARNING, Messages.getMessage(
867                    "deprecationWarning", "classesDirectory", "outputDirectory" ), null );
868
869            }
870
871            if ( !this.classesDirectory.equals( this.outputDirectory ) )
872            {
873                if ( this.isLoggable( Level.WARNING ) )
874                {
875                    this.log( Level.WARNING, Messages.getMessage( "ignoringParameter", "outputDirectory" ), null );
876                }
877
878                this.outputDirectory = this.classesDirectory;
879            }
880
881            this.classesDirectory = null;
882        }
883
884        final File dir = this.getAbsoluteFile( this.outputDirectory );
885        if ( !dir.exists() && !dir.mkdirs() )
886        {
887            throw new MojoExecutionException( Messages.getMessage( "failedCreatingDirectory", dir.getAbsolutePath() ) );
888        }
889
890        return dir;
891    }
892
893    /**
894     * Gets the directory holding the compiled test class files of the project.
895     *
896     * @return The directory holding the compiled test class files of the project.
897     *
898     * @throws MojoExecutionException if getting the directory fails.
899     *
900     * @since 1.1
901     */
902    protected File getTestOutputDirectory() throws MojoExecutionException
903    {
904        if ( this.testClassesDirectory != null )
905        {
906            if ( this.isLoggable( Level.WARNING ) )
907            {
908                this.log( Level.WARNING, Messages.getMessage(
909                    "deprecationWarning", "testClassesDirectory", "testOutputDirectory" ), null );
910
911            }
912
913            if ( !this.testClassesDirectory.equals( this.testOutputDirectory ) )
914            {
915                if ( this.isLoggable( Level.WARNING ) )
916                {
917                    this.log( Level.WARNING, Messages.getMessage( "ignoringParameter", "testOutputDirectory" ), null );
918                }
919
920                this.testOutputDirectory = this.testClassesDirectory;
921            }
922
923            this.testClassesDirectory = null;
924        }
925
926        final File dir = this.getAbsoluteFile( this.testOutputDirectory );
927        if ( !dir.exists() && !dir.mkdirs() )
928        {
929            throw new MojoExecutionException( Messages.getMessage( "failedCreatingDirectory", dir.getAbsolutePath() ) );
930        }
931
932        return dir;
933    }
934
935    /**
936     * Gets the directory holding the source files of the project.
937     *
938     * @return The directory holding the source files of the project.
939     *
940     * @throws MojoExecutionException if getting the directory fails.
941     *
942     * @since 1.1
943     */
944    protected File getSourceDirectory() throws MojoExecutionException
945    {
946        return this.getAbsoluteFile( this.sourceDirectory );
947    }
948
949    /**
950     * Gets the directory holding the test source files of the project.
951     *
952     * @return The directory holding the test source files of the project.
953     *
954     * @throws MojoExecutionException if getting the directory fails.
955     *
956     * @since 1.1
957     */
958    protected File getTestSourceDirectory() throws MojoExecutionException
959    {
960        return this.getAbsoluteFile( this.testSourceDirectory );
961    }
962
963    /**
964     * Gets the directory holding the session related files of the project.
965     *
966     * @return The directory holding the session related files of the project.
967     *
968     * @throws MojoExecutionException if getting the directory fails.
969     *
970     * @since 1.1
971     */
972    protected File getSessionDirectory() throws MojoExecutionException
973    {
974        return this.getAbsoluteFile( this.sessionDirectory );
975    }
976
977    /**
978     * Gets the directory holding the reports of the project.
979     *
980     * @return The directory holding the reports of the project.
981     *
982     * @throws MojoExecutionException if getting the directory fails.
983     *
984     * @since 1.1
985     */
986    protected File getReportOutputDirectory() throws MojoExecutionException
987    {
988        return this.getAbsoluteFile( this.reportOutputDirectory );
989    }
990
991    /**
992     * Gets the project's runtime class loader of the instance.
993     *
994     * @return The project's runtime class loader of the instance.
995     *
996     * @throws MojoExecutionException if getting the class loader fails.
997     */
998    protected ClassLoader getMainClassLoader() throws MojoExecutionException
999    {
1000        try
1001        {
1002            final Set<String> mainClasspathElements = this.getMainClasspathElements();
1003            final Set<URI> uris = new HashSet<URI>( mainClasspathElements.size() );
1004
1005            for ( String element : mainClasspathElements )
1006            {
1007                final URI uri = new File( element ).toURI();
1008                if ( !uris.contains( uri ) )
1009                {
1010                    uris.add( uri );
1011                }
1012            }
1013
1014            if ( this.isLoggable( Level.FINEST ) )
1015            {
1016                this.log( Level.FINEST, Messages.getMessage( "mainClasspathInfo" ), null );
1017            }
1018
1019            int i = 0;
1020            final URL[] urls = new URL[ uris.size() ];
1021            for ( URI uri : uris )
1022            {
1023                urls[i++] = uri.toURL();
1024
1025                if ( this.isLoggable( Level.FINEST ) )
1026                {
1027                    this.log( Level.FINEST, "\t" + urls[i - 1].toExternalForm(), null );
1028                }
1029            }
1030
1031            return new URLClassLoader( urls, Thread.currentThread().getContextClassLoader() );
1032        }
1033        catch ( final IOException e )
1034        {
1035            throw new MojoExecutionException( Messages.getMessage( e ), e );
1036        }
1037    }
1038
1039    /**
1040     * Gets the project's test class loader of the instance.
1041     *
1042     * @return The project's test class loader of the instance.
1043     *
1044     * @throws MojoExecutionException if getting the class loader fails.
1045     */
1046    protected ClassLoader getTestClassLoader() throws MojoExecutionException
1047    {
1048        try
1049        {
1050            final Set<String> testClasspathElements = this.getTestClasspathElements();
1051            final Set<URI> uris = new HashSet<URI>( testClasspathElements.size() );
1052
1053            for ( String element : testClasspathElements )
1054            {
1055                final URI uri = new File( element ).toURI();
1056                if ( !uris.contains( uri ) )
1057                {
1058                    uris.add( uri );
1059                }
1060            }
1061
1062            if ( this.isLoggable( Level.FINEST ) )
1063            {
1064                this.log( Level.FINEST, Messages.getMessage( "testClasspathInfo" ), null );
1065            }
1066
1067            int i = 0;
1068            final URL[] urls = new URL[ uris.size() ];
1069            for ( URI uri : uris )
1070            {
1071                urls[i++] = uri.toURL();
1072
1073                if ( this.isLoggable( Level.FINEST ) )
1074                {
1075                    this.log( Level.FINEST, "\t" + urls[i - 1].toExternalForm(), null );
1076                }
1077            }
1078
1079            return new URLClassLoader( urls, Thread.currentThread().getContextClassLoader() );
1080        }
1081        catch ( final IOException e )
1082        {
1083            throw new MojoExecutionException( Messages.getMessage( e ), e );
1084        }
1085    }
1086
1087    /**
1088     * Gets the project's runtime class path elements.
1089     *
1090     * @return A set of class path element strings.
1091     *
1092     * @throws MojoExecutionException if getting the class path elements fails.
1093     */
1094    protected Set<String> getMainClasspathElements() throws MojoExecutionException
1095    {
1096        final List<?> runtimeArtifacts = this.getMavenProject().getRuntimeArtifacts();
1097        final List<?> compileArtifacts = this.getMavenProject().getCompileArtifacts();
1098        final Set<String> elements = new HashSet<String>( runtimeArtifacts.size() + compileArtifacts.size() + 1 );
1099        elements.add( this.getOutputDirectory().getAbsolutePath() );
1100
1101        for ( final Iterator<?> it = runtimeArtifacts.iterator(); it.hasNext(); )
1102        {
1103            final Artifact a = (Artifact) it.next();
1104            final Artifact pluginArtifact = this.getPluginArtifact( a );
1105
1106            if ( a.getFile() == null )
1107            {
1108                if ( this.isLoggable( Level.WARNING ) )
1109                {
1110                    this.log( Level.WARNING, Messages.getMessage( "ignoringArtifact", a.toString() ), null );
1111                }
1112
1113                continue;
1114            }
1115
1116            if ( pluginArtifact != null )
1117            {
1118                if ( this.isLoggable( Level.FINER ) )
1119                {
1120                    this.log( Level.FINER, Messages.getMessage(
1121                        "ignoringPluginArtifact", a.toString(), pluginArtifact.toString() ), null );
1122
1123                }
1124
1125                continue;
1126            }
1127
1128            final String element = a.getFile().getAbsolutePath();
1129            elements.add( element );
1130        }
1131
1132        for ( final Iterator<?> it = compileArtifacts.iterator(); it.hasNext(); )
1133        {
1134            final Artifact a = (Artifact) it.next();
1135            final Artifact pluginArtifact = this.getPluginArtifact( a );
1136
1137            if ( a.getFile() == null )
1138            {
1139                if ( this.isLoggable( Level.WARNING ) )
1140                {
1141                    this.log( Level.WARNING, Messages.getMessage( "ignoringArtifact", a.toString() ), null );
1142                }
1143
1144                continue;
1145            }
1146
1147            if ( pluginArtifact != null )
1148            {
1149                if ( this.isLoggable( Level.FINER ) )
1150                {
1151                    this.log( Level.FINER, Messages.getMessage(
1152                        "ignoringPluginArtifact", a.toString(), pluginArtifact.toString() ), null );
1153
1154                }
1155
1156                continue;
1157            }
1158
1159            final String element = a.getFile().getAbsolutePath();
1160            elements.add( element );
1161        }
1162
1163        return elements;
1164    }
1165
1166    /**
1167     * Gets the project's test class path elements.
1168     *
1169     * @return A set of class path element strings.
1170     *
1171     * @throws MojoExecutionException if getting the class path elements fails.
1172     */
1173    protected Set<String> getTestClasspathElements() throws MojoExecutionException
1174    {
1175        final List<?> testArtifacts = this.getMavenProject().getTestArtifacts();
1176        final Set<String> elements = new HashSet<String>( testArtifacts.size() + 2 );
1177        elements.add( this.getOutputDirectory().getAbsolutePath() );
1178        elements.add( this.getTestOutputDirectory().getAbsolutePath() );
1179
1180        for ( final Iterator<?> it = testArtifacts.iterator(); it.hasNext(); )
1181        {
1182            final Artifact a = (Artifact) it.next();
1183            final Artifact pluginArtifact = this.getPluginArtifact( a );
1184
1185            if ( a.getFile() == null )
1186            {
1187                if ( this.isLoggable( Level.WARNING ) )
1188                {
1189                    this.log( Level.WARNING, Messages.getMessage( "ignoringArtifact", a.toString() ), null );
1190                }
1191
1192                continue;
1193            }
1194
1195            if ( pluginArtifact != null )
1196            {
1197                if ( this.isLoggable( Level.FINER ) )
1198                {
1199                    this.log( Level.FINER, Messages.getMessage(
1200                        "ignoringPluginArtifact", a.toString(), pluginArtifact.toString() ), null );
1201
1202                }
1203
1204                continue;
1205            }
1206
1207            final String element = a.getFile().getAbsolutePath();
1208            elements.add( element );
1209        }
1210
1211        return elements;
1212    }
1213
1214    /**
1215     * Gets a flag indicating verbose output is enabled.
1216     *
1217     * @return {@code true}, if verbose output is enabled; {@code false}, if information messages are suppressed.
1218     *
1219     * @throws MojoExecutionException if getting the flag fails.
1220     *
1221     * @since 1.1
1222     */
1223    protected final boolean isVerbose() throws MojoExecutionException
1224    {
1225        return this.verbose;
1226    }
1227
1228    /**
1229     * Sets the flag indicating verbose output is enabled.
1230     *
1231     * @param value {@code true}, to enable verbose output; {@code false}, to suppress information messages.
1232     *
1233     * @throws MojoExecutionException if setting the flag fails.
1234     *
1235     * @since 1.1
1236     */
1237    protected final void setVerbose( final boolean value ) throws MojoExecutionException
1238    {
1239        this.verbose = value;
1240    }
1241
1242    /**
1243     * Gets a flag indicating the processing of sources is enabled.
1244     *
1245     * @return {@code true}, if processing of sources is enabled; {@code false}, else.
1246     *
1247     * @throws MojoExecutionException if getting the flag fails.
1248     */
1249    protected final boolean isSourceProcessingEnabled() throws MojoExecutionException
1250    {
1251        return this.sourceProcessingEnabled;
1252    }
1253
1254    /**
1255     * Sets the flag indicating the processing of sources is enabled.
1256     *
1257     * @param value {@code true}, to enable processing of sources; {@code false}, to disable processing of sources.
1258     *
1259     * @throws MojoExecutionException if setting the flag fails.
1260     *
1261     * @since 1.1
1262     */
1263    protected final void setSourceProcessingEnabled( final boolean value ) throws MojoExecutionException
1264    {
1265        this.sourceProcessingEnabled = value;
1266    }
1267
1268    /**
1269     * Gets a flag indicating the processing of resources is enabled.
1270     *
1271     * @return {@code true}, if processing of resources is enabled; {@code false}, else.
1272     *
1273     * @throws MojoExecutionException if getting the flag fails.
1274     */
1275    protected final boolean isResourceProcessingEnabled() throws MojoExecutionException
1276    {
1277        return this.resourceProcessingEnabled;
1278    }
1279
1280    /**
1281     * Sets the flag indicating the processing of resources is enabled.
1282     *
1283     * @param value {@code true}, to enable processing of resources; {@code false}, to disable processing of resources.
1284     *
1285     * @throws MojoExecutionException if setting the flag fails.
1286     *
1287     * @since 1.1
1288     */
1289    protected final void setResourceProcessingEnabled( final boolean value ) throws MojoExecutionException
1290    {
1291        this.resourceProcessingEnabled = value;
1292    }
1293
1294    /**
1295     * Gets a flag indicating the processing of classes is enabled.
1296     *
1297     * @return {@code true}, if processing of classes is enabled; {@code false}, else.
1298     *
1299     * @throws MojoExecutionException if getting the flag fails.
1300     */
1301    protected final boolean isClassProcessingEnabled() throws MojoExecutionException
1302    {
1303        return this.classProcessingEnabled;
1304    }
1305
1306    /**
1307     * Sets the flag indicating the processing of classes is enabled.
1308     *
1309     * @param value {@code true}, to enable processing of classes; {@code false}, to disable processing of classes.
1310     *
1311     * @throws MojoExecutionException if setting the flag fails.
1312     *
1313     * @since 1.1
1314     */
1315    protected final void setClassProcessingEnabled( final boolean value ) throws MojoExecutionException
1316    {
1317        this.classProcessingEnabled = value;
1318    }
1319
1320    /**
1321     * Gets a flag indicating the processing of models is enabled.
1322     *
1323     * @return {@code true}, if processing of models is enabled; {@code false}, else.
1324     *
1325     * @throws MojoExecutionException if getting the flag fails.
1326     */
1327    protected final boolean isModelProcessingEnabled() throws MojoExecutionException
1328    {
1329        return this.modelProcessingEnabled;
1330    }
1331
1332    /**
1333     * Sets the flag indicating the processing of models is enabled.
1334     *
1335     * @param value {@code true}, to enable processing of models; {@code false}, to disable processing of models.
1336     *
1337     * @throws MojoExecutionException if setting the flag fails.
1338     *
1339     * @since 1.1
1340     */
1341    protected final void setModelProcessingEnabled( final boolean value ) throws MojoExecutionException
1342    {
1343        this.modelProcessingEnabled = value;
1344    }
1345
1346    /**
1347     * Gets a flag indicating model object class path resolution is enabled.
1348     *
1349     * @return {@code true}, if model object class path resolution is enabled; {@code false}, else.
1350     *
1351     * @throws MojoExecutionException if getting the flag fails.
1352     */
1353    protected final boolean isModelObjectClasspathResolutionEnabled() throws MojoExecutionException
1354    {
1355        return this.modelObjectClasspathResolutionEnabled;
1356    }
1357
1358    /**
1359     * Sets the flag indicating model object class path resolution is enabled.
1360     *
1361     * @param value {@code true}, to enable model object class path resolution; {@code false}, to disable model object
1362     * class path resolution.
1363     *
1364     * @throws MojoExecutionException if setting the flag fails.
1365     *
1366     * @since 1.1
1367     */
1368    protected final void setModelObjectClasspathResolutionEnabled( final boolean value ) throws MojoExecutionException
1369    {
1370        this.modelObjectClasspathResolutionEnabled = value;
1371    }
1372
1373    /**
1374     * Gets the identifier of the model to process.
1375     *
1376     * @return The identifier of the model to process.
1377     *
1378     * @throws MojoExecutionException if getting the identifier fails.
1379     */
1380    protected String getModel() throws MojoExecutionException
1381    {
1382        return this.model;
1383    }
1384
1385    /**
1386     * Gets the name of the module to process.
1387     *
1388     * @return The name of the module to process.
1389     *
1390     * @throws MojoExecutionException if getting the name of the module fails.
1391     */
1392    protected String getModuleName() throws MojoExecutionException
1393    {
1394        return this.moduleName;
1395    }
1396
1397    /**
1398     * Gets the name of the test module to process.
1399     *
1400     * @return The name of the test module to process.
1401     *
1402     * @throws MojoExecutionException if getting the name of the test module fails.
1403     */
1404    protected String getTestModuleName() throws MojoExecutionException
1405    {
1406        return this.testModuleName;
1407    }
1408
1409    /**
1410     * Gets the model to process.
1411     *
1412     * @param context The model context to get the model to process with.
1413     *
1414     * @return The model to process.
1415     *
1416     * @throws NullPointerException if {@code context} is {@code null}.
1417     * @throws MojoExecutionException if getting the model fails.
1418     */
1419    protected Model getModel( final ModelContext context ) throws MojoExecutionException
1420    {
1421        if ( context == null )
1422        {
1423            throw new NullPointerException( "context" );
1424        }
1425
1426        try
1427        {
1428            Model m = context.findModel( this.getModel() );
1429            final Modules modules = ModelHelper.getModules( m );
1430
1431            if ( modules != null && this.isModelObjectClasspathResolutionEnabled() )
1432            {
1433                final Module classpathModule =
1434                    modules.getClasspathModule( Modules.getDefaultClasspathModuleName(), context.getClassLoader() );
1435
1436                if ( classpathModule != null )
1437                {
1438                    modules.getModule().add( classpathModule );
1439                }
1440            }
1441
1442            if ( this.isModelProcessingEnabled() )
1443            {
1444                m = context.processModel( m );
1445            }
1446
1447            return m;
1448        }
1449        catch ( final ModelException e )
1450        {
1451            throw new MojoExecutionException( Messages.getMessage( e ), e );
1452        }
1453    }
1454
1455    /**
1456     * Creates a new model context instance for a given class loader.
1457     *
1458     * @param classLoader The class loader to use for creating the context.
1459     *
1460     * @return A new model context instance for {@code classLoader}.
1461     *
1462     * @throws MojoExecutionException if creating the model context fails.
1463     *
1464     * @see #setupModelContext(org.jomc.modlet.ModelContext)
1465     */
1466    protected ModelContext createModelContext( final ClassLoader classLoader ) throws MojoExecutionException
1467    {
1468        final ModelContextFactory modelContextFactory;
1469        if ( this.modelContextFactoryClassName != null )
1470        {
1471            modelContextFactory = ModelContextFactory.newInstance( this.modelContextFactoryClassName );
1472        }
1473        else
1474        {
1475            modelContextFactory = ModelContextFactory.newInstance();
1476        }
1477
1478        final ModelContext context = modelContextFactory.newModelContext( classLoader );
1479        this.setupModelContext( context );
1480
1481        return context;
1482    }
1483
1484    /**
1485     * Creates a new tool instance for processing source files.
1486     *
1487     * @param context The context of the tool.
1488     *
1489     * @return A new tool instance for processing source files.
1490     *
1491     * @throws NullPointerException if {@code context} is {@code null}.
1492     * @throws MojoExecutionException if creating a new tool instance fails.
1493     *
1494     * @see #createJomcTool(org.jomc.modlet.ModelContext, java.lang.String, java.lang.Class)
1495     */
1496    protected SourceFileProcessor createSourceFileProcessor( final ModelContext context ) throws MojoExecutionException
1497    {
1498        if ( context == null )
1499        {
1500            throw new NullPointerException( "context" );
1501        }
1502
1503        return this.createJomcTool( context, this.sourceFileProcessorClassName, SourceFileProcessor.class );
1504    }
1505
1506    /**
1507     * Creates a new tool instance for processing resource files.
1508     *
1509     * @param context The context of the tool.
1510     *
1511     * @return A new tool instance for processing resource files.
1512     *
1513     * @throws NullPointerException if {@code context} is {@code null}.
1514     * @throws MojoExecutionException if creating a new tool instance fails.
1515     *
1516     * @see #createJomcTool(org.jomc.modlet.ModelContext, java.lang.String, java.lang.Class)
1517     */
1518    protected ResourceFileProcessor createResourceFileProcessor( final ModelContext context )
1519        throws MojoExecutionException
1520    {
1521        if ( context == null )
1522        {
1523            throw new NullPointerException( "context" );
1524        }
1525
1526        return this.createJomcTool( context, this.resourceFileProcessorClassName, ResourceFileProcessor.class );
1527    }
1528
1529    /**
1530     * Creates a new tool instance for processing class files.
1531     *
1532     * @param context The context of the tool.
1533     *
1534     * @return A new tool instance for processing class files.
1535     *
1536     * @throws NullPointerException if {@code context} is {@code null}.
1537     * @throws MojoExecutionException if creating a new tool instance fails.
1538     *
1539     * @see #createJomcTool(org.jomc.modlet.ModelContext, java.lang.String, java.lang.Class)
1540     */
1541    protected ClassFileProcessor createClassFileProcessor( final ModelContext context ) throws MojoExecutionException
1542    {
1543        if ( context == null )
1544        {
1545            throw new NullPointerException( "context" );
1546        }
1547
1548        return this.createJomcTool( context, this.classFileProcessorClassName, ClassFileProcessor.class );
1549    }
1550
1551    /**
1552     * Creates a new {@code JomcTool} object for a given class name and type.
1553     *
1554     * @param context The context of the tool.
1555     * @param className The name of the class to create an object of.
1556     * @param type The class of the type of object to create.
1557     * @param <T> The type of the object to create.
1558     *
1559     * @return A new instance of the class with name {@code className}.
1560     *
1561     * @throws NullPointerException if {@code context}, {@code className} or {@code type} is {@code null}.
1562     * @throws MojoExecutionException if creating a new {@code JomcTool} object fails.
1563     *
1564     * @see #createObject(java.lang.String, java.lang.Class)
1565     * @see #setupJomcTool(org.jomc.modlet.ModelContext, org.jomc.tools.JomcTool)
1566     *
1567     * @since 1.2
1568     */
1569    protected <T extends JomcTool> T createJomcTool( final ModelContext context, final String className,
1570                                                     final Class<T> type ) throws MojoExecutionException
1571    {
1572        if ( context == null )
1573        {
1574            throw new NullPointerException( "context" );
1575        }
1576        if ( className == null )
1577        {
1578            throw new NullPointerException( "className" );
1579        }
1580        if ( type == null )
1581        {
1582            throw new NullPointerException( "type" );
1583        }
1584
1585        final T tool = this.createObject( className, type );
1586        this.setupJomcTool( context, tool );
1587        return tool;
1588    }
1589
1590    /**
1591     * Creates a new object for a given class name and type.
1592     *
1593     * @param className The name of the class to create an object of.
1594     * @param type The class of the type of object to create.
1595     * @param <T> The type of the object to create.
1596     *
1597     * @return A new instance of the class with name {@code className}.
1598     *
1599     * @throws NullPointerException if {@code className} or {@code type} is {@code null}.
1600     * @throws MojoExecutionException if creating a new object fails.
1601     *
1602     * @since 1.2
1603     */
1604    protected <T> T createObject( final String className, final Class<T> type ) throws MojoExecutionException
1605    {
1606        if ( className == null )
1607        {
1608            throw new NullPointerException( "className" );
1609        }
1610        if ( type == null )
1611        {
1612            throw new NullPointerException( "type" );
1613        }
1614
1615        try
1616        {
1617            return Class.forName( className ).asSubclass( type ).newInstance();
1618        }
1619        catch ( final InstantiationException e )
1620        {
1621            throw new MojoExecutionException( Messages.getMessage( "failedCreatingObject", className ), e );
1622        }
1623        catch ( final IllegalAccessException e )
1624        {
1625            throw new MojoExecutionException( Messages.getMessage( "failedCreatingObject", className ), e );
1626        }
1627        catch ( final ClassNotFoundException e )
1628        {
1629            throw new MojoExecutionException( Messages.getMessage( "failedCreatingObject", className ), e );
1630        }
1631        catch ( final ClassCastException e )
1632        {
1633            throw new MojoExecutionException( Messages.getMessage( "failedCreatingObject", className ), e );
1634        }
1635    }
1636
1637    /**
1638     * Creates an {@code URL} for a given resource location.
1639     * <p>This method first searches the class path of the plugin for a single resource matching {@code location}. If
1640     * such a resource is found, the URL of that resource is returned. If no such resource is found, an attempt is made
1641     * to parse the given location to an URL. On successful parsing, that URL is returned. Failing that, the given
1642     * location is interpreted as a file name relative to the project's base directory. If that file is found, the URL
1643     * of that file is returned. Otherwise {@code null} is returned.</p>
1644     *
1645     * @param location The location to create an {@code URL} from.
1646     *
1647     * @return An {@code URL} for {@code location} or {@code null}, if parsing {@code location} to an URL fails and
1648     * {@code location} points to a non-existent resource.
1649     *
1650     * @throws NullPointerException if {@code location} is {@code null}.
1651     * @throws MojoExecutionException if creating an URL fails.
1652     *
1653     * @since 1.2
1654     */
1655    protected URL getResource( final String location ) throws MojoExecutionException
1656    {
1657        if ( location == null )
1658        {
1659            throw new NullPointerException( "location" );
1660        }
1661
1662        try
1663        {
1664            String absolute = location;
1665            if ( !absolute.startsWith( "/" ) )
1666            {
1667                absolute = "/" + location;
1668            }
1669
1670            URL resource = this.getClass().getResource( absolute );
1671            if ( resource == null )
1672            {
1673                try
1674                {
1675                    resource = new URL( location );
1676                }
1677                catch ( final MalformedURLException e )
1678                {
1679                    if ( this.isLoggable( Level.FINEST ) )
1680                    {
1681                        this.log( Level.FINEST, Messages.getMessage( e ), e );
1682                    }
1683
1684                    resource = null;
1685                }
1686            }
1687
1688            if ( resource == null )
1689            {
1690                final File f = this.getAbsoluteFile( location );
1691
1692                if ( f.isFile() )
1693                {
1694                    resource = f.toURI().toURL();
1695                }
1696            }
1697
1698            return resource;
1699        }
1700        catch ( final MalformedURLException e )
1701        {
1702            String m = Messages.getMessage( e );
1703            m = m == null ? "" : " " + m;
1704
1705            throw new MojoExecutionException( Messages.getMessage( "malformedLocation", location, m ), e );
1706        }
1707    }
1708
1709    /**
1710     * Creates an {@code URL} for a given directory location.
1711     * <p>This method first attempts to parse the given location to an URL. On successful parsing, that URL is returned.
1712     * Failing that, the given location is interpreted as a directory name relative to the project's base directory.
1713     * If that directory is found, the URL of that directory is returned. Otherwise {@code null} is returned.</p>
1714     *
1715     * @param location The directory location to create an {@code URL} from.
1716     *
1717     * @return An {@code URL} for {@code location} or {@code null}, if parsing {@code location} to an URL fails and
1718     * {@code location} points to a non-existent directory.
1719     *
1720     * @throws NullPointerException if {@code location} is {@code null}.
1721     * @throws MojoExecutionException if creating an URL fails.
1722     *
1723     * @since 1.2
1724     */
1725    protected URL getDirectory( final String location ) throws MojoExecutionException
1726    {
1727        if ( location == null )
1728        {
1729            throw new NullPointerException( "location" );
1730        }
1731
1732        try
1733        {
1734            URL resource = null;
1735
1736            try
1737            {
1738                resource = new URL( location );
1739            }
1740            catch ( final MalformedURLException e )
1741            {
1742                if ( this.isLoggable( Level.FINEST ) )
1743                {
1744                    this.log( Level.FINEST, Messages.getMessage( e ), e );
1745                }
1746
1747                resource = null;
1748            }
1749
1750            if ( resource == null )
1751            {
1752                final File f = this.getAbsoluteFile( location );
1753
1754                if ( f.isDirectory() )
1755                {
1756                    resource = f.toURI().toURL();
1757                }
1758            }
1759
1760            return resource;
1761        }
1762        catch ( final MalformedURLException e )
1763        {
1764            String m = Messages.getMessage( e );
1765            m = m == null ? "" : " " + m;
1766
1767            throw new MojoExecutionException( Messages.getMessage( "malformedLocation", location, m ), e );
1768        }
1769    }
1770
1771    /**
1772     * Creates a new {@code Transformer} from a given {@code TransformerResourceType}.
1773     *
1774     * @param resource The resource to initialize the transformer with.
1775     *
1776     * @return A {@code Transformer} for {@code resource} or {@code null}, if {@code resource} is not found and flagged
1777     * optional.
1778     *
1779     * @throws NullPointerException if {@code resource} is {@code null}.
1780     * @throws MojoExecutionException if creating a transformer fails.
1781     *
1782     * @see #getResource(java.lang.String)
1783     * @since 1.2
1784     */
1785    protected Transformer getTransformer( final TransformerResourceType resource ) throws MojoExecutionException
1786    {
1787        if ( resource == null )
1788        {
1789            throw new NullPointerException( "resource" );
1790        }
1791
1792        InputStream in = null;
1793        boolean suppressExceptionOnClose = true;
1794        final URL url = this.getResource( resource.getLocation() );
1795        final ErrorListener errorListener = new ErrorListener()
1796        {
1797
1798            public void warning( final TransformerException exception ) throws TransformerException
1799            {
1800                try
1801                {
1802                    log( Level.WARNING, Messages.getMessage( exception ), exception );
1803                }
1804                catch ( final MojoExecutionException e )
1805                {
1806                    getLog().warn( exception );
1807                    getLog().error( e );
1808                }
1809            }
1810
1811            public void error( final TransformerException exception ) throws TransformerException
1812            {
1813                try
1814                {
1815                    log( Level.SEVERE, Messages.getMessage( exception ), exception );
1816                }
1817                catch ( final MojoExecutionException e )
1818                {
1819                    getLog().error( exception );
1820                    getLog().error( e );
1821                }
1822
1823                throw exception;
1824            }
1825
1826            public void fatalError( final TransformerException exception ) throws TransformerException
1827            {
1828                try
1829                {
1830                    log( Level.SEVERE, Messages.getMessage( exception ), exception );
1831                }
1832                catch ( final MojoExecutionException e )
1833                {
1834                    getLog().error( exception );
1835                    getLog().error( e );
1836                }
1837
1838                throw exception;
1839            }
1840
1841        };
1842
1843        try
1844        {
1845            if ( url != null )
1846            {
1847                if ( this.isLoggable( Level.FINER ) )
1848                {
1849                    this.log( Level.FINER, Messages.getMessage( "loadingTransformer", url.toExternalForm() ), null );
1850                }
1851
1852                final URLConnection con = url.openConnection();
1853                con.setConnectTimeout( resource.getConnectTimeout() );
1854                con.setReadTimeout( resource.getReadTimeout() );
1855                con.connect();
1856                in = con.getInputStream();
1857
1858                final TransformerFactory transformerFactory = TransformerFactory.newInstance();
1859                transformerFactory.setErrorListener( errorListener );
1860                final Transformer transformer =
1861                    transformerFactory.newTransformer( new StreamSource( in, url.toURI().toASCIIString() ) );
1862
1863                transformer.setErrorListener( errorListener );
1864
1865                for ( Map.Entry<Object, Object> e : System.getProperties().entrySet() )
1866                {
1867                    transformer.setParameter( e.getKey().toString(), e.getValue() );
1868                }
1869
1870                if ( this.getMavenProject().getProperties() != null )
1871                {
1872                    for ( Map.Entry<Object, Object> e : this.getMavenProject().getProperties().entrySet() )
1873                    {
1874                        transformer.setParameter( e.getKey().toString(), e.getValue() );
1875                    }
1876                }
1877
1878                if ( this.transformationParameterResources != null )
1879                {
1880                    for ( int i = 0, s0 = this.transformationParameterResources.size(); i < s0; i++ )
1881                    {
1882                        for ( Map.Entry<Object, Object> e :
1883                              this.getProperties( this.transformationParameterResources.get( i ) ).entrySet() )
1884                        {
1885                            transformer.setParameter( e.getKey().toString(), e.getValue() );
1886                        }
1887                    }
1888                }
1889
1890                if ( this.transformationParameters != null )
1891                {
1892                    for ( TransformationParameter e : this.transformationParameters )
1893                    {
1894                        transformer.setParameter( e.getKey(), e.getObject() );
1895                    }
1896                }
1897
1898                if ( this.transformationOutputProperties != null )
1899                {
1900                    for ( TransformationOutputProperty e : this.transformationOutputProperties )
1901                    {
1902                        transformer.setOutputProperty( e.getKey(), e.getValue() );
1903                    }
1904                }
1905
1906                for ( int i = 0, s0 = resource.getTransformationParameterResources().size(); i < s0; i++ )
1907                {
1908                    for ( Map.Entry<Object, Object> e :
1909                          this.getProperties( resource.getTransformationParameterResources().get( i ) ).entrySet() )
1910                    {
1911                        transformer.setParameter( e.getKey().toString(), e.getValue() );
1912                    }
1913                }
1914
1915                for ( TransformationParameter e : resource.getTransformationParameters() )
1916                {
1917                    transformer.setParameter( e.getKey(), e.getObject() );
1918                }
1919
1920                for ( TransformationOutputProperty e : resource.getTransformationOutputProperties() )
1921                {
1922                    transformer.setOutputProperty( e.getKey(), e.getValue() );
1923                }
1924
1925                suppressExceptionOnClose = false;
1926                return transformer;
1927            }
1928            else if ( resource.isOptional() )
1929            {
1930                if ( this.isLoggable( Level.WARNING ) )
1931                {
1932                    this.log( Level.WARNING, Messages.getMessage(
1933                        "transformerNotFound", resource.getLocation() ), null );
1934
1935                }
1936            }
1937            else
1938            {
1939                throw new MojoExecutionException( Messages.getMessage(
1940                    "transformerNotFound", resource.getLocation() ) );
1941
1942            }
1943        }
1944        catch ( final InstantiationException e )
1945        {
1946            throw new MojoExecutionException( Messages.getMessage( e ), e );
1947        }
1948        catch ( final URISyntaxException e )
1949        {
1950            throw new MojoExecutionException( Messages.getMessage( e ), e );
1951        }
1952        catch ( final TransformerConfigurationException e )
1953        {
1954            String m = Messages.getMessage( e );
1955            if ( m == null )
1956            {
1957                m = Messages.getMessage( e.getException() );
1958            }
1959
1960            m = m == null ? "" : " " + m;
1961
1962            throw new MojoExecutionException( Messages.getMessage(
1963                "failedCreatingTransformer", resource.getLocation(), m ), e );
1964
1965        }
1966        catch ( final SocketTimeoutException e )
1967        {
1968            String m = Messages.getMessage( e );
1969            m = m == null ? "" : " " + m;
1970
1971            if ( resource.isOptional() )
1972            {
1973                if ( this.isLoggable( Level.WARNING ) )
1974                {
1975                    this.log( Level.WARNING, Messages.getMessage(
1976                        "failedLoadingTransformer", url.toExternalForm(), m ), e );
1977
1978                }
1979            }
1980            else
1981            {
1982                throw new MojoExecutionException( Messages.getMessage(
1983                    "failedLoadingTransformer", url.toExternalForm(), m ), e );
1984
1985            }
1986        }
1987        catch ( final IOException e )
1988        {
1989            String m = Messages.getMessage( e );
1990            m = m == null ? "" : " " + m;
1991
1992            if ( resource.isOptional() )
1993            {
1994                if ( this.isLoggable( Level.WARNING ) )
1995                {
1996                    this.log( Level.WARNING, Messages.getMessage(
1997                        "failedLoadingTransformer", url.toExternalForm(), m ), e );
1998
1999                }
2000            }
2001            else
2002            {
2003                throw new MojoExecutionException( Messages.getMessage(
2004                    "failedLoadingTransformer", url.toExternalForm(), m ), e );
2005
2006            }
2007        }
2008        finally
2009        {
2010            try
2011            {
2012                if ( in != null )
2013                {
2014                    in.close();
2015                }
2016            }
2017            catch ( final IOException e )
2018            {
2019                if ( suppressExceptionOnClose )
2020                {
2021                    this.getLog().error( e );
2022                }
2023                else
2024                {
2025                    throw new MojoExecutionException( Messages.getMessage( e ), e );
2026                }
2027            }
2028        }
2029
2030        return null;
2031    }
2032
2033    /**
2034     * Creates a new {@code Properties} instance from a {@code PropertiesResourceType}.
2035     *
2036     * @param propertiesResourceType The {@code PropertiesResourceType} specifying the properties to create.
2037     *
2038     * @return The properties for {@code propertiesResourceType}.
2039     *
2040     * @throws NullPointerException if {@code propertiesResourceType} is {@code null}.
2041     * @throws MojoExecutionException if loading properties fails.
2042     *
2043     * @see #getResource(java.lang.String)
2044     * @since 1.2
2045     */
2046    protected Properties getProperties( final PropertiesResourceType propertiesResourceType )
2047        throws MojoExecutionException
2048    {
2049        if ( propertiesResourceType == null )
2050        {
2051            throw new NullPointerException( "propertiesResourceType" );
2052        }
2053
2054        InputStream in = null;
2055        boolean suppressExceptionOnClose = true;
2056        final URL url = this.getResource( propertiesResourceType.getLocation() );
2057        final Properties properties = new Properties();
2058
2059        try
2060        {
2061            if ( url != null )
2062            {
2063                if ( this.isLoggable( Level.FINER ) )
2064                {
2065                    this.log( Level.FINER, Messages.getMessage( "loadingProperties", url.toExternalForm() ), null );
2066                }
2067
2068                final URLConnection con = url.openConnection();
2069                con.setConnectTimeout( propertiesResourceType.getConnectTimeout() );
2070                con.setReadTimeout( propertiesResourceType.getReadTimeout() );
2071                con.connect();
2072
2073                in = con.getInputStream();
2074
2075                if ( PropertiesResourceType.PLAIN_FORMAT.equalsIgnoreCase( propertiesResourceType.getFormat() ) )
2076                {
2077                    properties.load( in );
2078                }
2079                else if ( PropertiesResourceType.XML_FORMAT.equalsIgnoreCase( propertiesResourceType.getFormat() ) )
2080                {
2081                    properties.loadFromXML( in );
2082                }
2083            }
2084            else if ( propertiesResourceType.isOptional() )
2085            {
2086                if ( this.isLoggable( Level.WARNING ) )
2087                {
2088                    this.log( Level.WARNING, Messages.getMessage(
2089                        "propertiesNotFound", propertiesResourceType.getLocation() ), null );
2090
2091                }
2092            }
2093            else
2094            {
2095                throw new MojoExecutionException( Messages.getMessage(
2096                    "propertiesNotFound", propertiesResourceType.getLocation() ) );
2097
2098            }
2099
2100            suppressExceptionOnClose = false;
2101        }
2102        catch ( final SocketTimeoutException e )
2103        {
2104            String m = Messages.getMessage( e );
2105            m = m == null ? "" : " " + m;
2106
2107            if ( propertiesResourceType.isOptional() )
2108            {
2109                if ( this.isLoggable( Level.WARNING ) )
2110                {
2111                    this.log( Level.WARNING, Messages.getMessage(
2112                        "failedLoadingProperties", url.toExternalForm(), m ), e );
2113
2114                }
2115            }
2116            else
2117            {
2118                throw new MojoExecutionException( Messages.getMessage(
2119                    "failedLoadingProperties", url.toExternalForm(), m ), e );
2120
2121            }
2122        }
2123        catch ( final IOException e )
2124        {
2125            String m = Messages.getMessage( e );
2126            m = m == null ? "" : " " + m;
2127
2128            if ( propertiesResourceType.isOptional() )
2129            {
2130                if ( this.isLoggable( Level.WARNING ) )
2131                {
2132                    this.log( Level.WARNING, Messages.getMessage(
2133                        "failedLoadingProperties", url.toExternalForm(), m ), e );
2134
2135                }
2136            }
2137            else
2138            {
2139                throw new MojoExecutionException( Messages.getMessage(
2140                    "failedLoadingProperties", url.toExternalForm(), m ), e );
2141
2142            }
2143        }
2144        finally
2145        {
2146            try
2147            {
2148                if ( in != null )
2149                {
2150                    in.close();
2151                }
2152            }
2153            catch ( final IOException e )
2154            {
2155                if ( suppressExceptionOnClose )
2156                {
2157                    this.getLog().error( e );
2158                }
2159                else
2160                {
2161                    throw new MojoExecutionException( Messages.getMessage( e ), e );
2162                }
2163            }
2164        }
2165
2166        return properties;
2167    }
2168
2169    /**
2170     * Tests if messages at a given level are logged.
2171     *
2172     * @param level The level to test.
2173     *
2174     * @return {@code true}, if messages at {@code level} are logged; {@code false}, if messages at {@code level} are
2175     * suppressed.
2176     *
2177     * @throws NullPointerException if {@code level} is {@code null}.
2178     * @throws MojoExecutionException if testing the level fails.
2179     *
2180     * @see #isVerbose()
2181     * @since 1.2
2182     */
2183    protected boolean isLoggable( final Level level ) throws MojoExecutionException
2184    {
2185        if ( level == null )
2186        {
2187            throw new NullPointerException( "level" );
2188        }
2189
2190        boolean loggable = false;
2191
2192        if ( level.intValue() <= Level.CONFIG.intValue() )
2193        {
2194            loggable = this.getLog().isDebugEnabled();
2195        }
2196        else if ( level.intValue() <= Level.INFO.intValue() )
2197        {
2198            loggable = this.getLog().isInfoEnabled() && this.isVerbose();
2199        }
2200        else if ( level.intValue() <= Level.WARNING.intValue() )
2201        {
2202            loggable = this.getLog().isWarnEnabled();
2203        }
2204        else if ( level.intValue() <= Level.SEVERE.intValue() )
2205        {
2206            loggable = this.getLog().isErrorEnabled();
2207        }
2208
2209        return loggable;
2210    }
2211
2212    /**
2213     * Logs a separator at a given level.
2214     *
2215     * @param level The level to log a separator at.
2216     *
2217     * @throws MojoExecutionException if logging fails.
2218     *
2219     * @deprecated As of JOMC 1.1, please use method {@link #logSeparator()}. This method will be removed in version
2220     * 2.0.
2221     */
2222    @Deprecated
2223    protected void logSeparator( final Level level ) throws MojoExecutionException
2224    {
2225        this.logSeparator();
2226    }
2227
2228    /**
2229     * Logs a separator.
2230     *
2231     * @throws MojoExecutionException if logging fails.
2232     *
2233     * @since 1.1
2234     */
2235    protected void logSeparator() throws MojoExecutionException
2236    {
2237        if ( this.isLoggable( Level.INFO ) )
2238        {
2239            this.log( Level.INFO, Messages.getMessage( "separator" ), null );
2240        }
2241    }
2242
2243    /**
2244     * Logs a message stating a tool is starting to process a module.
2245     *
2246     * @param toolName The tool starting execution.
2247     * @param module The module getting processed.
2248     *
2249     * @throws MojoExecutionException if logging fails.
2250     */
2251    protected void logProcessingModule( final String toolName, final String module ) throws MojoExecutionException
2252    {
2253        if ( this.isLoggable( Level.INFO ) )
2254        {
2255            this.log( Level.INFO, Messages.getMessage( "processingModule", toolName, module ), null );
2256        }
2257    }
2258
2259    /**
2260     * Logs a message stating a tool is starting to process a model.
2261     *
2262     * @param toolName The tool starting execution.
2263     * @param model The model getting processed.
2264     *
2265     * @throws MojoExecutionException if logging fails.
2266     *
2267     * @since 1.1
2268     */
2269    protected void logProcessingModel( final String toolName, final String model ) throws MojoExecutionException
2270    {
2271        if ( this.isLoggable( Level.INFO ) )
2272        {
2273            this.log( Level.INFO, Messages.getMessage( "processingModel", toolName, model ), null );
2274        }
2275    }
2276
2277    /**
2278     * Logs a message stating that a module has not been found.
2279     *
2280     * @param module The module not having been found.
2281     *
2282     * @throws MojoExecutionException if logging fails.
2283     */
2284    protected void logMissingModule( final String module ) throws MojoExecutionException
2285    {
2286        if ( this.isLoggable( Level.WARNING ) )
2287        {
2288            this.log( Level.WARNING, Messages.getMessage( "missingModule", module ), null );
2289        }
2290    }
2291
2292    /**
2293     * Logs a message stating that a tool successfully completed execution.
2294     *
2295     * @param toolName The name of the tool.
2296     *
2297     * @throws MojoExecutionException if logging fails.
2298     */
2299    protected void logToolSuccess( final String toolName ) throws MojoExecutionException
2300    {
2301        if ( this.isLoggable( Level.INFO ) )
2302        {
2303            this.log( Level.INFO, Messages.getMessage( "toolSuccess", toolName ), null );
2304        }
2305    }
2306
2307    /**
2308     * Logs a {@code ModelValidationReport}.
2309     *
2310     * @param context The context to use when marshalling detail elements of the report.
2311     * @param level The level to log at.
2312     * @param report The report to log.
2313     *
2314     * @throws MojoExecutionException if logging {@code report} fails.
2315     */
2316    protected void log( final ModelContext context, final Level level, final ModelValidationReport report )
2317        throws MojoExecutionException
2318    {
2319        try
2320        {
2321            if ( !report.getDetails().isEmpty() )
2322            {
2323                this.logSeparator();
2324                Marshaller marshaller = null;
2325
2326                for ( ModelValidationReport.Detail detail : report.getDetails() )
2327                {
2328                    this.log( detail.getLevel(), "o " + detail.getMessage(), null );
2329
2330                    if ( detail.getElement() != null && this.isLoggable( Level.FINEST ) )
2331                    {
2332                        if ( marshaller == null )
2333                        {
2334                            marshaller = context.createMarshaller( this.getModel() );
2335                            marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
2336                        }
2337
2338                        final StringWriter stringWriter = new StringWriter();
2339                        marshaller.marshal( detail.getElement(), stringWriter );
2340                        this.log( Level.FINEST, stringWriter.toString(), null );
2341                    }
2342                }
2343            }
2344        }
2345        catch ( final ModelException e )
2346        {
2347            throw new MojoExecutionException( Messages.getMessage( e ), e );
2348        }
2349        catch ( final JAXBException e )
2350        {
2351            String message = Messages.getMessage( e );
2352            if ( message == null && e.getLinkedException() != null )
2353            {
2354                message = Messages.getMessage( e.getLinkedException() );
2355            }
2356
2357            throw new MojoExecutionException( message, e );
2358        }
2359    }
2360
2361    /**
2362     * Logs a message and throwable at a given level.
2363     *
2364     * @param level The level to log at.
2365     * @param message The message to log or {@code null}.
2366     * @param throwable The throwable to log or {@code null}.
2367     *
2368     * @throws MojoExecutionException if logging fails.
2369     */
2370    protected void log( final Level level, final String message, final Throwable throwable )
2371        throws MojoExecutionException
2372    {
2373        BufferedReader reader = null;
2374        boolean suppressExceptionOnClose = true;
2375
2376        try
2377        {
2378            if ( this.isLoggable( level ) )
2379            {
2380                String line;
2381                reader = new BufferedReader( new StringReader( message == null ? "" : message ) );
2382                boolean throwableLogged = false;
2383
2384                while ( ( line = reader.readLine() ) != null )
2385                {
2386                    final String mojoMessage =
2387                        Messages.getMessage( this.getLog().isDebugEnabled() ? "debugMessage" : "logMessage", line,
2388                                             Thread.currentThread().getName(), new Date( System.currentTimeMillis() ) );
2389
2390                    if ( level.intValue() <= Level.CONFIG.intValue() )
2391                    {
2392                        this.getLog().debug( mojoMessage, throwableLogged ? null : throwable );
2393                    }
2394                    else if ( level.intValue() <= Level.INFO.intValue() )
2395                    {
2396                        this.getLog().info( mojoMessage, throwableLogged ? null : throwable );
2397                    }
2398                    else if ( level.intValue() <= Level.WARNING.intValue() )
2399                    {
2400                        this.getLog().warn( mojoMessage, throwableLogged ? null : throwable );
2401                    }
2402                    else if ( level.intValue() <= Level.SEVERE.intValue() )
2403                    {
2404                        this.getLog().error( mojoMessage, throwableLogged ? null : throwable );
2405                    }
2406
2407                    throwableLogged = true;
2408                }
2409            }
2410
2411            suppressExceptionOnClose = false;
2412        }
2413        catch ( final IOException e )
2414        {
2415            this.getLog().error( e );
2416            throw new AssertionError( e );
2417        }
2418        finally
2419        {
2420            try
2421            {
2422                if ( reader != null )
2423                {
2424                    reader.close();
2425                }
2426            }
2427            catch ( final IOException e )
2428            {
2429                if ( !suppressExceptionOnClose )
2430                {
2431                    throw new AssertionError( e );
2432                }
2433            }
2434        }
2435    }
2436
2437    /**
2438     * Configures a {@code ModelContext} instance.
2439     *
2440     * @param context The model context to configure.
2441     *
2442     * @throws NullPointerException if {@code context} is {@code null}.
2443     * @throws MojoExecutionException if configuring {@code context} fails.
2444     */
2445    protected void setupModelContext( final ModelContext context ) throws MojoExecutionException
2446    {
2447        if ( context == null )
2448        {
2449            throw new NullPointerException( "context" );
2450        }
2451
2452        if ( this.isVerbose() || this.getLog().isDebugEnabled() )
2453        {
2454            context.setLogLevel( this.getLog().isDebugEnabled() ? Level.ALL : Level.INFO );
2455        }
2456
2457        try
2458        {
2459            context.setModletSchemaSystemId( this.modletSchemaSystemId );
2460            context.getListeners().add( new ModelContext.Listener()
2461            {
2462
2463                @Override
2464                public void onLog( final Level level, final String message, final Throwable t )
2465                {
2466                    super.onLog( level, message, t );
2467
2468                    try
2469                    {
2470                        log( level, message, t );
2471                    }
2472                    catch ( final MojoExecutionException e )
2473                    {
2474                        getLog().error( e );
2475                    }
2476                }
2477
2478            } );
2479
2480            if ( this.providerLocation != null )
2481            {
2482                context.setAttribute( DefaultModelContext.PROVIDER_LOCATION_ATTRIBUTE_NAME, this.providerLocation );
2483            }
2484
2485            if ( this.platformProviderLocation != null )
2486            {
2487                context.setAttribute( DefaultModelContext.PLATFORM_PROVIDER_LOCATION_ATTRIBUTE_NAME,
2488                                      this.platformProviderLocation );
2489
2490            }
2491
2492            if ( this.modletLocation != null )
2493            {
2494                context.setAttribute( DefaultModletProvider.MODLET_LOCATION_ATTRIBUTE_NAME, this.modletLocation );
2495            }
2496
2497            if ( this.transformerLocation != null )
2498            {
2499                context.setAttribute( DefaultModelProcessor.TRANSFORMER_LOCATION_ATTRIBUTE_NAME,
2500                                      this.transformerLocation );
2501            }
2502
2503            if ( this.moduleLocation != null )
2504            {
2505                context.setAttribute( DefaultModelProvider.MODULE_LOCATION_ATTRIBUTE_NAME, this.moduleLocation );
2506            }
2507
2508            context.setAttribute( ToolsModelProvider.MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME,
2509                                  this.modelObjectClasspathResolutionEnabled );
2510
2511            context.setAttribute( ToolsModelProcessor.MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME,
2512                                  this.modelObjectClasspathResolutionEnabled );
2513
2514            context.setAttribute( DefaultModletProvider.VALIDATING_ATTRIBUTE_NAME, this.modletResourceValidationEnabled );
2515            context.setAttribute( DefaultModelProvider.VALIDATING_ATTRIBUTE_NAME, this.modelResourceValidationEnabled );
2516
2517            if ( this.modelContextAttributes != null )
2518            {
2519                for ( ModelContextAttribute e : this.modelContextAttributes )
2520                {
2521                    final Object object = e.getObject();
2522
2523                    if ( object != null )
2524                    {
2525                        context.setAttribute( e.getKey(), object );
2526                    }
2527                    else
2528                    {
2529                        context.clearAttribute( e.getKey() );
2530                    }
2531                }
2532            }
2533        }
2534        catch ( final InstantiationException e )
2535        {
2536            throw new MojoExecutionException( Messages.getMessage( e ), e );
2537        }
2538    }
2539
2540    /**
2541     * Configures a {@code JomcTool} instance.
2542     *
2543     * @param context The model context to use for configuring {@code tool}.
2544     * @param tool The tool to configure.
2545     *
2546     * @throws NullPointerException if {@code context} of {@code tool} is {@code null}.
2547     * @throws MojoExecutionException if configuring {@code tool} fails.
2548     */
2549    protected void setupJomcTool( final ModelContext context, final JomcTool tool ) throws MojoExecutionException
2550    {
2551        if ( context == null )
2552        {
2553            throw new NullPointerException( "context" );
2554        }
2555        if ( tool == null )
2556        {
2557            throw new NullPointerException( "tool" );
2558        }
2559
2560        try
2561        {
2562            if ( this.isVerbose() || this.getLog().isDebugEnabled() )
2563            {
2564                tool.setLogLevel( this.getLog().isDebugEnabled() ? Level.ALL : Level.INFO );
2565            }
2566
2567            tool.getListeners().add( new JomcTool.Listener()
2568            {
2569
2570                @Override
2571                public void onLog( final Level level, final String message, final Throwable t )
2572                {
2573                    super.onLog( level, message, t );
2574
2575                    try
2576                    {
2577                        log( level, message, t );
2578                    }
2579                    catch ( final MojoExecutionException e )
2580                    {
2581                        getLog().error( e );
2582                    }
2583                }
2584
2585            } );
2586
2587            if ( this.templateEncoding != null )
2588            {
2589                if ( this.isLoggable( Level.WARNING ) )
2590                {
2591                    this.log( Level.WARNING, Messages.getMessage(
2592                        "deprecationWarning", "templateEncoding", "defaultTemplateEncoding" ), null );
2593
2594                }
2595
2596                tool.setDefaultTemplateEncoding( this.templateEncoding );
2597            }
2598            else
2599            {
2600                tool.setDefaultTemplateEncoding( this.defaultTemplateEncoding );
2601            }
2602
2603            tool.setInputEncoding( this.sourceEncoding );
2604            tool.setOutputEncoding( this.sourceEncoding );
2605            tool.setDefaultTemplateProfile( this.defaultTemplateProfile );
2606            tool.setTemplateProfile( this.templateProfile );
2607            tool.setModel( this.getModel( context ) );
2608
2609            if ( this.indentation != null )
2610            {
2611                tool.setIndentation( StringEscapeUtils.unescapeJava( this.indentation ) );
2612            }
2613
2614            if ( this.lineSeparator != null )
2615            {
2616                tool.setLineSeparator( StringEscapeUtils.unescapeJava( this.lineSeparator ) );
2617            }
2618
2619            if ( this.locale != null )
2620            {
2621                tool.setLocale( new Locale( StringUtils.defaultString( this.locale.getLanguage() ),
2622                                            StringUtils.defaultString( this.locale.getCountry() ),
2623                                            StringUtils.defaultString( this.locale.getVariant() ) ) );
2624
2625            }
2626
2627            if ( this.velocityPropertyResources != null )
2628            {
2629                for ( int i = 0, s0 = this.velocityPropertyResources.size(); i < s0; i++ )
2630                {
2631                    for ( Map.Entry<Object, Object> e :
2632                          this.getProperties( this.velocityPropertyResources.get( i ) ).entrySet() )
2633                    {
2634                        if ( e.getValue() != null )
2635                        {
2636                            tool.getVelocityEngine().setProperty( e.getKey().toString(), e );
2637                        }
2638                        else
2639                        {
2640                            tool.getVelocityEngine().clearProperty( e.getKey().toString() );
2641                        }
2642                    }
2643                }
2644            }
2645
2646            if ( this.velocityProperties != null )
2647            {
2648                for ( VelocityProperty e : this.velocityProperties )
2649                {
2650                    final Object object = e.getObject();
2651
2652                    if ( object != null )
2653                    {
2654                        tool.getVelocityEngine().setProperty( e.getKey(), object );
2655                    }
2656                    else
2657                    {
2658                        tool.getVelocityEngine().clearProperty( e.getKey() );
2659                    }
2660                }
2661            }
2662
2663            for ( Map.Entry<Object, Object> e : System.getProperties().entrySet() )
2664            {
2665                tool.getTemplateParameters().put( e.getKey().toString(), e.getValue() );
2666            }
2667
2668            if ( this.getMavenProject().getProperties() != null )
2669            {
2670                for ( Map.Entry<Object, Object> e : System.getProperties().entrySet() )
2671                {
2672                    tool.getTemplateParameters().put( e.getKey().toString(), e.getValue() );
2673                }
2674            }
2675
2676            if ( this.templateParameterResources != null )
2677            {
2678                for ( int i = 0, s0 = this.templateParameterResources.size(); i < s0; i++ )
2679                {
2680                    for ( Map.Entry<Object, Object> e :
2681                          this.getProperties( this.templateParameterResources.get( i ) ).entrySet() )
2682                    {
2683                        if ( e.getValue() != null )
2684                        {
2685                            tool.getTemplateParameters().put( e.getKey().toString(), e.getValue() );
2686                        }
2687                        else
2688                        {
2689                            tool.getTemplateParameters().remove( e.getKey().toString() );
2690                        }
2691                    }
2692                }
2693            }
2694
2695            if ( this.templateParameters != null )
2696            {
2697                for ( TemplateParameter e : this.templateParameters )
2698                {
2699                    final Object object = e.getObject();
2700
2701                    if ( object != null )
2702                    {
2703                        tool.getTemplateParameters().put( e.getKey(), object );
2704                    }
2705                    else
2706                    {
2707                        tool.getTemplateParameters().remove( e.getKey() );
2708                    }
2709                }
2710            }
2711
2712            if ( this.templateLocation != null )
2713            {
2714                final URL url = this.getDirectory( this.templateLocation );
2715                tool.setTemplateLocation( url );
2716
2717                if ( url == null && this.isLoggable( Level.WARNING ) )
2718                {
2719                    this.log( Level.WARNING, Messages.getMessage( "locationNotFound", this.templateLocation ), null );
2720                }
2721            }
2722        }
2723        catch ( final InstantiationException e )
2724        {
2725            throw new MojoExecutionException( Messages.getMessage( e ), e );
2726        }
2727        catch ( final IOException e )
2728        {
2729            throw new MojoExecutionException( Messages.getMessage( e ), e );
2730        }
2731    }
2732
2733    private Artifact getPluginArtifact( final Artifact a )
2734    {
2735        for ( int i = 0, s0 = this.pluginArtifacts.size(); i < s0; i++ )
2736        {
2737            final Artifact pluginArtifact = this.pluginArtifacts.get( i );
2738
2739            if ( pluginArtifact.getGroupId().equals( a.getGroupId() )
2740                 && pluginArtifact.getArtifactId().equals( a.getArtifactId() )
2741                 && ( pluginArtifact.hasClassifier()
2742                      ? pluginArtifact.getClassifier().equals( a.getClassifier() )
2743                      : !a.hasClassifier() ) )
2744            {
2745                return pluginArtifact;
2746            }
2747        }
2748
2749        return null;
2750    }
2751
2752}