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