View Javadoc

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