View Javadoc

1   /*
2    *  jDTAUS Core Container Mojo
3    *  Copyright (C) 2005 Christian Schulte
4    *  <cs@schulte.it>
5    *
6    *  This library is free software; you can redistribute it and/or
7    *  modify it under the terms of the GNU Lesser General Public
8    *  License as published by the Free Software Foundation; either
9    *  version 2.1 of the License, or any later version.
10   *
11   *  This library is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   *  Lesser General Public License for more details.
15   *
16   *  You should have received a copy of the GNU Lesser General Public
17   *  License along with this library; if not, write to the Free Software
18   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19   *
20   */
21  package org.jdtaus.core.container.mojo;
22  
23  import java.io.File;
24  import java.io.FileOutputStream;
25  import java.io.FileWriter;
26  import java.io.IOException;
27  import java.io.OutputStream;
28  import java.io.OutputStreamWriter;
29  import java.io.Writer;
30  import java.text.ParseException;
31  import java.util.List;
32  import java.util.Locale;
33  import javax.xml.bind.JAXBException;
34  import org.apache.maven.plugin.MojoExecutionException;
35  import org.apache.maven.plugin.MojoFailureException;
36  import org.apache.velocity.VelocityContext;
37  import org.jdtaus.core.container.Argument;
38  import org.jdtaus.core.container.ContainerError;
39  import org.jdtaus.core.container.ContextError;
40  import org.jdtaus.core.container.Dependencies;
41  import org.jdtaus.core.container.Dependency;
42  import org.jdtaus.core.container.Implementation;
43  import org.jdtaus.core.container.Implementations;
44  import org.jdtaus.core.container.Message;
45  import org.jdtaus.core.container.Messages;
46  import org.jdtaus.core.container.MissingModuleException;
47  import org.jdtaus.core.container.Model;
48  import org.jdtaus.core.container.ModelError;
49  import org.jdtaus.core.container.ModelFactory;
50  import org.jdtaus.core.container.Module;
51  import org.jdtaus.core.container.Properties;
52  import org.jdtaus.core.container.Property;
53  import org.jdtaus.core.container.Specification;
54  import org.jdtaus.core.container.Specifications;
55  import org.jdtaus.core.container.mojo.comp.VersionParser;
56  import org.jdtaus.core.container.mojo.model.JavaArtifact;
57  
58  /**
59   * Mojo to generate java code.
60   *
61   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
62   * @version $JDTAUS: JavaContainerMojo.java 8743 2012-10-07 03:06:20Z schulte $
63   * @goal java-container
64   * @phase process-resources
65   * @requiresDependencyResolution test
66   */
67  public class JavaContainerMojo extends AbstractContainerMojo
68  {
69      //--Configuration-----------------------------------------------------------
70  
71      /** Model version 1.4. */
72      private static final String V1_4 = "1.4";
73  
74      /**
75       * Output directory.
76       * @parameter expression="${project.build.directory}/container"
77       */
78      private File outputDirectory;
79  
80      /**
81       * The name of the module to process.
82       * @parameter expression="${moduleName}" default-value="${project.name}"
83       */
84      private String moduleName;
85  
86      /**
87       * The name of the test module to process.
88       * @parameter expression="${testModuleName}"
89       *            default-value="${project.name} Tests"
90       */
91      private String testModuleName;
92  
93      /**
94       * The string marking the starting line where to start inserting specification
95       * code.
96       * @parameter expression="${specificationsStartingMarker}" default-value="//--Specification-----------------------------------------------------------"
97       */
98      private String specificationsStartingMarker;
99  
100     /**
101      * The string marking the ending line up to where specification code should
102      * be inserted.
103      * @parameter expression="${specificationsEndingMarker}" default-value="//-----------------------------------------------------------Specification--"
104      */
105     private String specificationsEndingMarker;
106 
107     /**
108      * The string marking the starting line where to start inserting implementation
109      * code.
110      * @parameter expression="${implementationsStartingMarker}" default-value="//--Implementation----------------------------------------------------------"
111      */
112     private String implementationsStartingMarker;
113 
114     /**
115      * The string marking the ending line up to where implementation code should
116      * be inserted.
117      * @parameter expression="${implementationsEndingMarker}" default-value="//----------------------------------------------------------Implementation--"
118      */
119     private String implementationsEndingMarker;
120 
121     /**
122      * The string marking the starting line where to start inserting constructor
123      * code.
124      * @parameter expression="${constructorsStartingMarker}" default-value="//--Constructors------------------------------------------------------------"
125      */
126     private String constructorsStartingMarker;
127 
128     /**
129      * The string marking the ending line up to where constructor code should be
130      * inserted.
131      * @parameter expression="${constructorsEndingMarker}" default-value="//------------------------------------------------------------Constructors--"
132      */
133     private String constructorsEndingMarker;
134 
135     /**
136      * The string marking the starting line where to start inserting dependency
137      * code.
138      * @parameter expression="${dependenciesStartingMarker}" default-value="//--Dependencies------------------------------------------------------------"
139      */
140     private String dependenciesStartingMarker;
141 
142     /**
143      * The string marking the ending line up to where dependency code should be
144      * inserted.
145      * @parameter expression="${dependenciesEndingMarker}" default-value="//------------------------------------------------------------Dependencies--"
146      */
147     private String dependenciesEndingMarker;
148 
149     /**
150      * The string marking the starting line where to start inserting
151      * implementation property code.
152      * @parameter expression="${propertiesStartingMarker}" default-value="//--Properties--------------------------------------------------------------"
153      */
154     private String propertiesStartingMarker;
155 
156     /**
157      * The string marking the ending line up to where implementation property
158      * code should be inserted.
159      * @parameter expression="${propertiesEndingMarker}" default-value="//--------------------------------------------------------------Properties--"
160      */
161     private String propertiesEndingMarker;
162 
163     /**
164      * The string marking the starting line where to start inserting
165      * implementation message code.
166      * @parameter expression="${messagesStartingMarker}" default-value="//--Messages----------------------------------------------------------------"
167      */
168     private String messagesStartingMarker;
169 
170     /**
171      * The string marking the ending line up to where implementation message
172      * code should be inserted.
173      * @parameter expression="${messagesEndingMarker}" default-value="//----------------------------------------------------------------Messages--"
174      */
175     private String messagesEndingMarker;
176 
177     /**
178      * Specifies the target editor used for editing sourcefiles. Used for e.g.
179      * generating folding markers specific to IDE editors. Currently available
180      * options are {@code none} and {@code netbeans}.
181      *
182      * @parameter expression="${targetIde}" default-value="netbeans"
183      */
184     private String targetIde;
185 
186     /**
187      * Source root to create new source files in.
188      * @parameter expression="${sourceRoot}"
189      *            default-value="${basedir}/src/main/java"
190      */
191     private File sourceRoot;
192 
193     /** Cached model. */
194     protected Model model;
195 
196     /**
197      * Gets the model of the current execution.
198      *
199      * @return The model of the current execution.
200      */
201     protected Model getModel()
202     {
203         if ( this.model == null )
204         {
205             this.model = ModelFactory.newModel();
206         }
207 
208         return this.model;
209     }
210 
211     /**
212      * Accessor to the currently executed jDTAUS module.
213      *
214      * @return The currently executed jDTAUS module or {@code null} if no module
215      * is defined for the currently executed jDTAUS module.
216      */
217     protected final Module getModule()
218     {
219         Module module = null;
220 
221         if ( this.moduleName != null )
222         {
223             try
224             {
225                 module = this.getModel().getModules().
226                     getModule( this.moduleName );
227 
228                 if ( this.getLog().isDebugEnabled() )
229                 {
230                     this.getLog().debug( module.toString() );
231                 }
232             }
233             catch ( final MissingModuleException e )
234             {
235                 this.getLog().info( JavaContainerMojoBundle.getInstance().
236                     getSkippingMainModuleMessage( Locale.getDefault() ) );
237 
238             }
239         }
240 
241         return module;
242     }
243 
244     /**
245      * Accessor to the currently executed jDTAUS test module.
246      *
247      * @return The currently executed jDTAUS test module or {@code null} if
248      * no test module is defined for the currently executed jDTAUS module.
249      */
250     protected final Module getTestModule()
251     {
252         Module module = null;
253 
254         if ( this.testModuleName != null )
255         {
256             try
257             {
258                 module = this.getModel().getModules().
259                     getModule( this.testModuleName );
260 
261                 if ( this.getLog().isDebugEnabled() )
262                 {
263                     this.getLog().debug( module.toString() );
264                 }
265             }
266             catch ( final MissingModuleException e )
267             {
268                 this.getLog().info( JavaContainerMojoBundle.getInstance().
269                     getSkippingTestModuleMessage( Locale.getDefault() ) );
270 
271             }
272         }
273 
274         return module;
275     }
276 
277     /**
278      * Gets the target editor to use when generating source code.
279      *
280      * @return The target editor to use when generating source code.
281      */
282     protected final String getTargetEditor()
283     {
284         return this.targetIde.toLowerCase();
285     }
286 
287     /**
288      * Gets the source root to create new source files in.
289      *
290      * @return The source root to create new source files in.
291      */
292     protected final File getSourceRoot()
293     {
294         return this.sourceRoot;
295     }
296 
297     /**
298      * Gets the output directory of the mojo.
299      *
300      * @return The output directory of the mojo.
301      */
302     protected final File getOutputDirectory()
303     {
304         return this.outputDirectory;
305     }
306 
307     //-----------------------------------------------------------Configuration--
308     //--AbstractMojo------------------------------------------------------------
309 
310     /** {@inheritDoc} */
311     public void execute() throws MojoExecutionException, MojoFailureException
312     {
313         final ClassLoader mavenLoader = Thread.currentThread().
314             getContextClassLoader();
315 
316         try
317         {
318             this.model = null;
319 
320             Thread.currentThread().setContextClassLoader(
321                 this.getRuntimeClassLoader( mavenLoader ) );
322 
323             enableThreadContextClassLoader();
324 
325             final Module mod = this.getModule();
326 
327             if ( mod != null )
328             {
329                 final Specifications specs = mod.getSpecifications();
330                 final Implementations impls = mod.getImplementations();
331 
332                 for ( int i = specs.size() - 1; i >= 0; i-- )
333                 {
334                     this.generateSpecification(
335                         this.getMavenProject().getCompileSourceRoots(),
336                         specs.getSpecification( i ) );
337 
338                 }
339 
340                 for ( int i = impls.size() - 1; i >= 0; i-- )
341                 {
342                     this.generateImplementation(
343                         this.getMavenProject().getCompileSourceRoots(),
344                         impls.getImplementation( i ) );
345 
346                 }
347 
348                 this.writeContainerReport( this.getModel(),
349                                            "container-report.xml" );
350 
351                 this.getLog().info( JavaContainerMojoBundle.getInstance().
352                     getProcessingModuleMessage( Locale.getDefault(),
353                                                 mod.getName() ) );
354 
355             }
356         }
357         catch ( final ContextError e )
358         {
359             throw new MojoExecutionException( e.getMessage(), e );
360         }
361         catch ( final ContainerError e )
362         {
363             throw new MojoExecutionException( e.getMessage(), e );
364         }
365         catch ( final ModelError e )
366         {
367             throw new MojoExecutionException( e.getMessage(), e );
368         }
369         catch ( final Exception e )
370         {
371             throw new MojoExecutionException( e.getMessage(), e );
372         }
373         finally
374         {
375             disableThreadContextClassLoader();
376             Thread.currentThread().setContextClassLoader( mavenLoader );
377         }
378     }
379 
380     //------------------------------------------------------------AbstractMojo--
381     //--ContainerMojo-----------------------------------------------------------
382 
383     /** Location of the implementation template. */
384     private static final String IMPLEMENTATION_TEMPLATE_LOCATION =
385         "META-INF/templates/Implementation.java.vm";
386 
387     /** Creates a new {@code JavaContainerMojo} instance. */
388     public JavaContainerMojo()
389     {
390         super();
391     }
392 
393     /** Adds dependency getters to an implementation. */
394     private final class DependencyEditor
395         implements AbstractContainerMojo.SourceEditor
396     {
397 
398         private boolean editing;
399 
400         private boolean modified;
401 
402         private final String fileName;
403 
404         private final Implementation impl;
405 
406         private final boolean markersNeeded;
407 
408         private DependencyEditor( final String fileName,
409                                   final Implementation impl )
410         {
411             if ( fileName == null )
412             {
413                 throw new NullPointerException( "fileName" );
414             }
415             if ( impl == null )
416             {
417                 throw new NullPointerException( "impl" );
418             }
419 
420             this.fileName = fileName;
421             this.impl = impl;
422             this.markersNeeded = impl.getDeclaredDependencies().size() > 0;
423         }
424 
425         public String editLine( final String line ) throws MojoFailureException
426         {
427             // Replace with nothing by default.
428             String replacement = null;
429 
430             if ( line == null && this.editing )
431             {
432                 throw new MojoFailureException(
433                     JavaContainerMojoBundle.getInstance().
434                     getUnexpectedEndOfInputMessage( Locale.getDefault(),
435                                                     this.fileName ) );
436 
437             }
438 
439             if ( line != null &&
440                  dependenciesStartingMarker.equals( line.trim() ) )
441             {
442                 // Skip all input up to the ending marker.
443                 this.editing = true;
444                 final StringBuffer buf = new StringBuffer( 1024 );
445                 buf.append( line ).append( "\n\n" );
446                 if ( !getTargetEditor().equals( "none" ) )
447                 {
448                     buf.append( getOpeningFoldingMarker( "Dependencies" ) ).
449                         append( '\n' );
450 
451                 }
452 
453                 indent( buf );
454                 buf.append( JavaContainerMojoBundle.getInstance().
455                     getGeneratorWarningMessage( getLocale() ) );
456 
457                 buf.append( "\n\n" );
458 
459                 // Generate dependency getter.
460                 if ( this.isMarkersNeeded() )
461                 {
462                     this.modified = true;
463                     final Dependencies deps =
464                         this.impl.getDeclaredDependencies();
465 
466                     for ( int i = deps.size() - 1; i >= 0; i-- )
467                     {
468                         final Dependency dep = deps.getDependency( i );
469                         String depType = getTypeFromClassName(
470                             dep.getSpecification().getIdentifier() );
471 
472                         if ( dep.getImplementation() == null &&
473                              dep.getSpecification().getMultiplicity() ==
474                              Specification.MULTIPLICITY_MANY )
475                         {
476                             depType += "[]";
477                         }
478 
479                         final boolean hasDescription = dep.getDocumentation().
480                             getLocales().length > 0;
481 
482                         final String description =
483                             hasDescription
484                             ? dep.getDocumentation().getValue( getLocale() )
485                             : JavaContainerMojoBundle.getInstance().
486                             getDefaultDependencyDescriptionMessage(
487                             getLocale(), dep.getName() );
488 
489                         indent( buf );
490                         buf.append( JavaContainerMojoBundle.getInstance().
491                             getDependencyGetterCommentMessage(
492                             getLocale(), dep.getName(), description ) );
493 
494                         indent( buf );
495                         buf.append(
496                             this.impl.isFinal() && this.impl.getParent() == null
497                             ? "private "
498                             : "protected " ).append( depType ).
499                             append( " " );
500 
501                         buf.append( getModelManager().
502                             getJavaGetterMethodName( dep ) ).
503                             append( "()\n" );
504 
505                         indent( buf );
506                         buf.append( "{\n" );
507 
508                         indent( buf );
509                         indent( buf );
510                         buf.append( "return (" ).append( depType ).
511                             append( ") " ).
512                             append( "ContainerFactory.getContainer().\n" );
513 
514                         indent( buf );
515                         indent( buf );
516                         indent( buf );
517                         buf.append( "getDependency( this, \"" ).append(
518                             dep.getName() ).
519                             append( "\" );\n\n" );
520 
521                         indent( buf );
522                         buf.append( "}\n\n" );
523                     }
524                 }
525 
526                 if ( !getTargetEditor().equals( "none" ) )
527                 {
528                     buf.append( getClosingFoldingMarker( "Dependencies" ) ).
529                         append( '\n' );
530 
531                 }
532 
533                 replacement = buf.toString();
534             }
535             else
536             {
537                 if ( this.editing )
538                 {
539                     if ( dependenciesEndingMarker.equals( line.trim() ) )
540                     {
541                         this.editing = false;
542                         replacement = line;
543                     }
544                 }
545                 else
546                 {
547                     replacement = line;
548                 }
549             }
550 
551             return replacement;
552         }
553 
554         public boolean isModified()
555         {
556             return this.modified;
557         }
558 
559         public boolean isMarkersNeeded()
560         {
561             return this.markersNeeded;
562         }
563 
564     }
565 
566     /** Adds property getters to an implementation. */
567     private final class PropertyEditor
568         implements AbstractContainerMojo.SourceEditor
569     {
570 
571         private boolean editing;
572 
573         private boolean modified;
574 
575         private final String fileName;
576 
577         private final Implementation impl;
578 
579         private final boolean markersNeeded;
580 
581         private PropertyEditor( final String fileName,
582                                 final Implementation impl )
583         {
584             if ( fileName == null )
585             {
586                 throw new NullPointerException( "fileName" );
587             }
588             if ( impl == null )
589             {
590                 throw new NullPointerException( "impl" );
591             }
592 
593             this.fileName = fileName;
594             this.impl = impl;
595             this.markersNeeded = impl.getDeclaredProperties().size() > 0;
596         }
597 
598         public String editLine( final String line ) throws MojoFailureException
599         {
600             // Replace with nothing by default.
601             String replacement = null;
602 
603             if ( line == null && this.editing )
604             {
605                 throw new MojoFailureException(
606                     JavaContainerMojoBundle.getInstance().
607                     getUnexpectedEndOfInputMessage( Locale.getDefault(),
608                                                     this.fileName ) );
609 
610             }
611 
612             if ( line != null &&
613                  propertiesStartingMarker.equals( line.trim() ) )
614             {
615                 // Skip all input up to the ending marker.
616                 this.editing = true;
617                 final StringBuffer buf = new StringBuffer( 1024 );
618                 buf.append( line ).append( "\n\n" );
619 
620                 if ( !getTargetEditor().equals( "none" ) )
621                 {
622                     buf.append( getOpeningFoldingMarker( "Properties" ) ).
623                         append( '\n' );
624 
625                 }
626 
627                 indent( buf );
628                 buf.append( JavaContainerMojoBundle.getInstance().
629                     getGeneratorWarningMessage( getLocale() ) );
630                 buf.append( "\n\n" );
631 
632                 // Generate property getters and setters.
633                 if ( this.isMarkersNeeded() )
634                 {
635                     this.modified = true;
636                     this.generateProperties( this.impl.getDeclaredProperties(),
637                                              buf );
638 
639                 }
640 
641                 if ( !getTargetEditor().equals( "none" ) )
642                 {
643                     buf.append( getClosingFoldingMarker( "Properties" ) ).
644                         append( '\n' );
645 
646                 }
647 
648                 replacement = buf.toString();
649             }
650             else
651             {
652                 if ( this.editing )
653                 {
654                     if ( propertiesEndingMarker.equals( line.trim() ) )
655                     {
656                         this.editing = false;
657                         replacement = line;
658                     }
659                 }
660                 else
661                 {
662                     replacement = line;
663                 }
664             }
665 
666             return replacement;
667         }
668 
669         public boolean isModified()
670         {
671             return this.modified;
672         }
673 
674         public boolean isMarkersNeeded()
675         {
676             return this.markersNeeded;
677         }
678 
679         private void generateProperties( final Properties properties,
680                                          final StringBuffer buf )
681         {
682             for ( int i = properties.size() - 1; i >= 0; i-- )
683             {
684                 final Property property = properties.getProperty( i );
685 
686                 // Getter.
687                 final boolean hasDescription = property.getDocumentation().
688                     getLocales().length > 0;
689 
690                 final String description = hasDescription
691                                            ? property.getDocumentation().
692                     getValue( getLocale() )
693                                            : JavaContainerMojoBundle.getInstance().
694                     getDefaultPropertyDescriptionMessage( getLocale(),
695                                                           property.getName() );
696 
697                 indent( buf );
698                 buf.append( JavaContainerMojoBundle.getInstance().
699                     getPropertyGetterCommentMessage(
700                     getLocale(), property.getName(),
701                     formatComment( description ) ) ).append( '\n' );
702 
703                 indent( buf );
704                 buf.append( property.isApi()
705                             ? "public "
706                             : ( this.impl.isFinal() &&
707                                 this.impl.getParent() == null
708                                 ? "private "
709                                 : "protected " ) );
710 
711                 buf.append( property.getType().getName() ).append( " " ).
712                     append( getModelManager().
713                     getJavaGetterMethodName( property ) ).
714                     append( "()\n" );
715 
716                 indent( buf );
717                 buf.append( "{\n" );
718 
719                 indent( buf );
720                 indent( buf );
721                 buf.append( "return " );
722 
723                 if ( property.getType().isPrimitive() )
724                 {
725                     buf.append( "( (" ).
726                         append( property.getValue().getClass().getName() ).
727                         append( ") ContainerFactory.getContainer().\n" );
728 
729                     indent( buf );
730                     indent( buf );
731                     indent( buf );
732                     buf.append( "getProperty( this, \"" ).
733                         append( property.getName() ).
734                         append( "\" ) )." ).
735                         append( property.getType().getName() ).
736                         append( "Value();\n\n" );
737 
738                 }
739                 else
740                 {
741                     buf.append( "(" ).append( property.getType().getName() ).
742                         append( ") ContainerFactory.getContainer().\n" );
743 
744                     indent( buf );
745                     indent( buf );
746                     indent( buf );
747                     buf.append( "getProperty( this, \"" ).
748                         append( property.getName() ).
749                         append( "\" );\n\n" );
750 
751                 }
752 
753                 indent( buf );
754                 buf.append( "}\n\n" );
755             }
756         }
757 
758     }
759 
760     /** Adds implementation constructors. */
761     private final class ConstructorsEditor
762         implements AbstractContainerMojo.SourceEditor
763     {
764 
765         private boolean editing;
766 
767         private boolean modified;
768 
769         private final String fileName;
770 
771         private final Implementation impl;
772 
773         private final boolean markersNeeded;
774 
775         private ConstructorsEditor(
776             final String fileName, final Implementation impl )
777             throws MojoFailureException
778         {
779             if ( fileName == null )
780             {
781                 throw new NullPointerException( "fileName" );
782             }
783             if ( impl == null )
784             {
785                 throw new NullPointerException( "impl" );
786             }
787 
788             this.fileName = fileName;
789             this.impl = impl;
790             this.markersNeeded = impl.getImplementedSpecifications().size() > 0;
791         }
792 
793         public String editLine( final String line ) throws MojoFailureException
794         {
795             // Replace with nothing by default.
796             String replacement = null;
797 
798             if ( line == null && this.editing )
799             {
800                 throw new MojoFailureException(
801                     JavaContainerMojoBundle.getInstance().
802                     getUnexpectedEndOfInputMessage( Locale.getDefault(),
803                                                     this.fileName ) );
804 
805             }
806 
807             if ( line != null &&
808                  constructorsStartingMarker.equals( line.trim() ) )
809             {
810                 // Skip all input up to the ending marker.
811                 this.editing = true;
812                 this.modified = true;
813                 final StringBuffer buf = new StringBuffer( 1024 );
814                 final String implType =
815                     getTypeFromClassName( this.impl.getIdentifier() );
816 
817                 buf.append( line ).append( "\n\n" );
818                 if ( !getTargetEditor().equals( "none" ) )
819                 {
820                     buf.append( getOpeningFoldingMarker( "Constructors" ) ).
821                         append( '\n' );
822 
823                 }
824 
825                 indent( buf );
826                 buf.append( JavaContainerMojoBundle.getInstance().
827                     getGeneratorWarningMessage( getLocale() ) );
828                 buf.append( "\n\n" );
829 
830                 if ( this.impl.getImplementedSpecifications().size() > 0 )
831                 {
832                     indent( buf );
833                     buf.append(
834                         JavaContainerMojoBundle.getInstance().
835                         getStandardConstructorMessage(
836                         getLocale(), this.impl.getIdentifier(), implType ) );
837 
838                     buf.append( '\n' );
839                 }
840 
841                 if ( !getTargetEditor().equals( "none" ) )
842                 {
843                     buf.append( getClosingFoldingMarker( "Constructors" ) ).
844                         append( '\n' );
845 
846                 }
847 
848                 replacement = buf.toString();
849             }
850             else
851             {
852                 if ( this.editing )
853                 {
854                     if ( constructorsEndingMarker.equals( line.trim() ) )
855                     {
856                         this.editing = false;
857                         replacement = line;
858                     }
859                 }
860                 else
861                 {
862                     replacement = line;
863                 }
864             }
865 
866             return replacement;
867         }
868 
869         public boolean isModified()
870         {
871             return this.modified;
872         }
873 
874         public boolean isMarkersNeeded()
875         {
876             return this.markersNeeded;
877         }
878 
879     }
880 
881     /** Adds message getters to an implementation. */
882     private final class MessageEditor
883         implements AbstractContainerMojo.SourceEditor
884     {
885 
886         private boolean editing;
887 
888         private boolean modified;
889 
890         private final String fileName;
891 
892         private final Implementation impl;
893 
894         private final boolean markersNeeded;
895 
896         public MessageEditor( final String fileName,
897                               final Implementation impl )
898         {
899             if ( fileName == null )
900             {
901                 throw new NullPointerException( "fileName" );
902             }
903             if ( impl == null )
904             {
905                 throw new NullPointerException( "impl" );
906             }
907 
908             this.fileName = fileName;
909             this.impl = impl;
910             this.markersNeeded = impl.getDeclaredMessages().size() > 0;
911         }
912 
913         public String editLine( final String line ) throws MojoFailureException
914         {
915             // Replace with nothing by default.
916             String replacement = null;
917 
918             if ( line == null && this.editing )
919             {
920                 throw new MojoFailureException(
921                     JavaContainerMojoBundle.getInstance().
922                     getUnexpectedEndOfInputMessage( Locale.getDefault(),
923                                                     this.fileName ) );
924 
925             }
926 
927             if ( line != null &&
928                  messagesStartingMarker.equals( line.trim() ) )
929             {
930                 // Skip all input up to the ending marker.
931                 this.editing = true;
932                 final StringBuffer buf = new StringBuffer( 1024 );
933                 buf.append( line ).append( "\n\n" );
934 
935                 if ( !getTargetEditor().equals( "none" ) )
936                 {
937                     buf.append( getOpeningFoldingMarker( "Messages" ) ).
938                         append( '\n' );
939 
940                 }
941 
942                 indent( buf );
943                 buf.append( JavaContainerMojoBundle.getInstance().
944                     getGeneratorWarningMessage( getLocale() ) );
945                 buf.append( "\n\n" );
946 
947                 // Generate property getters and setters.
948                 if ( this.isMarkersNeeded() )
949                 {
950                     this.modified = true;
951                     this.generateMessages( this.impl.getDeclaredMessages(),
952                                            buf );
953 
954                 }
955 
956                 if ( !getTargetEditor().equals( "none" ) )
957                 {
958                     buf.append( getClosingFoldingMarker( "Messages" ) ).
959                         append( '\n' );
960 
961                 }
962 
963                 replacement = buf.toString();
964             }
965             else
966             {
967                 if ( this.editing )
968                 {
969                     if ( messagesEndingMarker.equals( line.trim() ) )
970                     {
971                         this.editing = false;
972                         replacement = line;
973                     }
974                 }
975                 else
976                 {
977                     replacement = line;
978                 }
979             }
980 
981             return replacement;
982         }
983 
984         public boolean isModified()
985         {
986             return this.modified;
987         }
988 
989         public boolean isMarkersNeeded()
990         {
991             return this.markersNeeded;
992         }
993 
994         private void generateMessages( final Messages messages,
995                                        final StringBuffer buf )
996             throws MojoFailureException
997         {
998             try
999             {
1000                 for ( int i = messages.size() - 1; i >= 0; i-- )
1001                 {
1002                     final Message message = messages.getMessage( i );
1003 
1004                     // Getter.
1005                     final boolean hasDescription = message.getDocumentation().
1006                         getLocales().length > 0;
1007 
1008                     final String description = hasDescription
1009                                                ? message.getDocumentation().
1010                         getValue( getLocale() )
1011                                                : JavaContainerMojoBundle.
1012                         getInstance().
1013                         getDefaultMessageDescriptionMessage( getLocale(),
1014                                                              message.getName() );
1015 
1016                     indent( buf );
1017                     buf.append( "/**\n" );
1018                     indent( buf );
1019                     buf.append( " * " );
1020                     buf.append( JavaContainerMojoBundle.getInstance().
1021                         getMessageGetterCommentMessage( getLocale(),
1022                                                         message.getName() ) ).
1023                         append( '\n' );
1024 
1025                     final Locale[] locales = message.getTemplate().getLocales();
1026                     for ( int d = locales.length - 1; d >= 0; d-- )
1027                     {
1028                         indent( buf );
1029                         buf.append( " * <blockquote><pre>" );
1030                         buf.append( formatComment( message.getTemplate().
1031                             getValue( locales[d] ) ) );
1032 
1033                         buf.append( "</pre></blockquote>\n" );
1034                     }
1035 
1036                     if ( message.getArguments().size() > 0 ||
1037                          VersionParser.compare( messages.getModelVersion(),
1038                                                 V1_4 ) >= 0 )
1039                     {
1040                         indent( buf );
1041                         buf.append( " *\n" );
1042 
1043                         if ( VersionParser.compare( messages.getModelVersion(),
1044                                                     V1_4 ) >= 0 )
1045                         {
1046                             indent( buf );
1047                             buf.append( " * @param locale " ).
1048                                 append( formatComment(
1049                                 JavaContainerMojoBundle.getInstance().
1050                                 getLocaleParamCommentMessage( getLocale() ) ) ).
1051                                 append( '\n' );
1052 
1053                         }
1054 
1055                         for ( int a = 0; a < message.getArguments().size();
1056                               a++ )
1057                         {
1058                             indent( buf );
1059                             buf.append( " * @param " ).
1060                                 append( message.getArguments().getArgument( a ).
1061                                 getName() ).append( " " );
1062 
1063                             if ( message.getArguments().getArgument( a ).
1064                                 getDocumentation().getLocales().length > 0 )
1065                             {
1066                                 buf.append(
1067                                     formatComment(
1068                                     message.getArguments().getArgument( a ).
1069                                     getDocumentation().
1070                                     getValue( getLocale() ) ) );
1071 
1072                             }
1073                             else
1074                             {
1075                                 buf.append( formatComment(
1076                                     JavaContainerMojoBundle.getInstance().
1077                                     getDefaultArgumentDescriptionMessage(
1078                                     getLocale() ) ) );
1079 
1080                             }
1081 
1082                             buf.append( '\n' );
1083                         }
1084                     }
1085 
1086                     indent( buf );
1087                     buf.append( " *\n" );
1088 
1089                     indent( buf );
1090                     buf.append( " * @return " );
1091                     buf.append( formatComment( description ) ).append( '\n' );
1092                     indent( buf );
1093                     buf.append( " */\n" );
1094 
1095                     indent( buf );
1096                     buf.append( this.impl.isFinal() &&
1097                                 this.impl.getParent() == null
1098                                 ? "private" : "protected" );
1099 
1100                     buf.append( " String " ).append(
1101                         getModelManager().getJavaGetterMethodName( message ) );
1102 
1103                     buf.append( "(" );
1104                     if ( VersionParser.compare( messages.getModelVersion(),
1105                                                 V1_4 ) >= 0 )
1106                     {
1107                         buf.append( " final Locale locale" );
1108                         if ( message.getArguments().size() > 0 )
1109                         {
1110                             buf.append( ',' );
1111                         }
1112                     }
1113 
1114                     if ( message.getArguments().size() > 0 )
1115                     {
1116                         buf.append( "\n" );
1117                         indent( buf );
1118                         indent( buf );
1119                         indent( buf );
1120 
1121                         for ( int a = 0; a < message.getArguments().size();
1122                               a++ )
1123                         {
1124                             final Argument arg = message.getArguments().
1125                                 getArgument( a );
1126 
1127                             final String javaType;
1128                             switch ( arg.getType() )
1129                             {
1130                                 case Argument.TYPE_DATE:
1131                                 case Argument.TYPE_TIME:
1132                                     javaType = "java.util.Date";
1133                                     break;
1134 
1135                                 case Argument.TYPE_NUMBER:
1136                                     javaType = "java.lang.Number";
1137                                     break;
1138 
1139                                 case Argument.TYPE_TEXT:
1140                                     javaType = "java.lang.String";
1141                                     break;
1142 
1143                                 default:
1144                                     throw new AssertionError(
1145                                         Integer.toString( arg.getType() ) );
1146 
1147                             }
1148 
1149                             buf.append( "final " ).append( javaType ).
1150                                 append( " " ).append( arg.getName() );
1151 
1152                             if ( a + 1 < message.getArguments().size() )
1153                             {
1154                                 buf.append( ",\n" );
1155                                 indent( buf );
1156                                 indent( buf );
1157                                 indent( buf );
1158                             }
1159                             else
1160                             {
1161                                 buf.append( " )\n" );
1162                             }
1163                         }
1164                     }
1165                     else
1166                     {
1167                         if ( VersionParser.compare( messages.getModelVersion(),
1168                                                     V1_4 ) >= 0 )
1169                         {
1170                             buf.append( " " );
1171                         }
1172 
1173                         buf.append( ")\n" );
1174                     }
1175 
1176                     indent( buf );
1177                     buf.append( "{\n" );
1178 
1179                     indent( buf );
1180                     indent( buf );
1181                     buf.append( "return ContainerFactory.getContainer().\n" );
1182                     indent( buf );
1183                     indent( buf );
1184                     indent( buf );
1185                     buf.append( "getMessage( this, \"" ).
1186                         append( message.getName() ).append( '"' );
1187 
1188                     if ( VersionParser.compare( messages.getModelVersion(),
1189                                                 V1_4 ) >= 0 )
1190                     {
1191                         buf.append( ", locale" );
1192                     }
1193 
1194                     if ( message.getArguments().size() > 0 )
1195                     {
1196                         buf.append( ",\n" );
1197                         indent( buf );
1198                         indent( buf );
1199                         indent( buf );
1200                         indent( buf );
1201                         buf.append( "new Object[]\n" );
1202                         indent( buf );
1203                         indent( buf );
1204                         indent( buf );
1205                         indent( buf );
1206                         buf.append( "{\n" );
1207 
1208                         for ( int a = 0; a < message.getArguments().size(); a++ )
1209                         {
1210                             final Argument arg =
1211                                 message.getArguments().getArgument( a );
1212 
1213                             indent( buf );
1214                             indent( buf );
1215                             indent( buf );
1216                             indent( buf );
1217                             indent( buf );
1218                             buf.append( arg.getName() );
1219 
1220                             if ( a + 1 < message.getArguments().size() )
1221                             {
1222                                 buf.append( "," );
1223                             }
1224 
1225                             buf.append( "\n" );
1226                         }
1227 
1228                         indent( buf );
1229                         indent( buf );
1230                         indent( buf );
1231                         indent( buf );
1232                         buf.append( "});\n\n" );
1233                     }
1234                     else
1235                     {
1236                         buf.append( ", null );\n\n" );
1237                     }
1238 
1239                     indent( buf );
1240                     buf.append( "}\n\n" );
1241                 }
1242             }
1243             catch ( final ParseException e )
1244             {
1245                 throw (MojoFailureException) new MojoFailureException(
1246                     e.getMessage() ).initCause( e );
1247 
1248             }
1249         }
1250 
1251     }
1252 
1253     /** Cleans a section. */
1254     private final class RemovingEditor
1255         implements AbstractContainerMojo.SourceEditor
1256     {
1257 
1258         private boolean editing;
1259 
1260         private boolean modified;
1261 
1262         private final String fileName;
1263 
1264         private final String startingMarker;
1265 
1266         private final String endingMarker;
1267 
1268         private RemovingEditor( final String fileName,
1269                                 final String startingMarker,
1270                                 final String endingMarker )
1271         {
1272             if ( fileName == null )
1273             {
1274                 throw new NullPointerException( "fileName" );
1275             }
1276             if ( startingMarker == null )
1277             {
1278                 throw new NullPointerException( "startingMarker" );
1279             }
1280             if ( endingMarker == null )
1281             {
1282                 throw new NullPointerException( "endingMarker" );
1283             }
1284 
1285             this.fileName = fileName;
1286             this.startingMarker = startingMarker;
1287             this.endingMarker = endingMarker;
1288         }
1289 
1290         public String editLine( final String line ) throws MojoFailureException
1291         {
1292             if ( line == null && this.editing )
1293             {
1294                 throw new MojoFailureException(
1295                     JavaContainerMojoBundle.getInstance().
1296                     getUnexpectedEndOfInputMessage( Locale.getDefault(),
1297                                                     this.fileName ) );
1298 
1299             }
1300 
1301             // Replace with nothing by default.
1302             String replacement = null;
1303 
1304             if ( line != null && this.startingMarker.equals( line.trim() ) )
1305             {
1306                 // Skip all input up to the ending marker.
1307                 this.editing = true;
1308                 this.modified = true;
1309                 // Leave the marker.
1310                 final StringBuffer buf = new StringBuffer( 1024 );
1311                 buf.append( line ).append( "\n\n" );
1312 
1313                 indent( buf );
1314                 buf.append( JavaContainerMojoBundle.getInstance().
1315                     getGeneratorWarningMessage( getLocale() ) );
1316                 buf.append( "\n\n" );
1317 
1318                 replacement = buf.toString();
1319             }
1320             else
1321             {
1322                 if ( this.editing )
1323                 {
1324                     if ( this.endingMarker.equals( line.trim() ) )
1325                     {
1326                         // Stop editing.
1327                         this.editing = false;
1328                         // Leave the ending marker.
1329                         replacement = line;
1330                     }
1331                 }
1332                 else
1333                 {
1334                     replacement = line;
1335                 }
1336             }
1337 
1338             return replacement;
1339 
1340         }
1341 
1342         public boolean isModified()
1343         {
1344             return this.modified;
1345         }
1346 
1347     }
1348 
1349     protected String getTypeFromClassName( final String className )
1350     {
1351         if ( className == null )
1352         {
1353             throw new NullPointerException( "className" );
1354         }
1355 
1356         return className.substring( className.lastIndexOf( '.' ) + 1 );
1357     }
1358 
1359     protected void generateImplementation( final List roots,
1360                                            final Implementation impl )
1361         throws MojoExecutionException, MojoFailureException
1362     {
1363         Writer writer = null;
1364 
1365         try
1366         {
1367             if ( roots == null )
1368             {
1369                 throw new NullPointerException( "roots" );
1370             }
1371 
1372             if ( impl == null )
1373             {
1374                 throw new NullPointerException( "impl" );
1375             }
1376 
1377             String edited;
1378             File source = this.getSource( roots, impl.getIdentifier() );
1379 
1380             if ( source == null )
1381             {
1382                 final JavaArtifact artifact =
1383                     new JavaArtifact( impl.getIdentifier() );
1384 
1385                 source = new File( this.getSourceRoot(),
1386                                    artifact.getPackagePath() + File.separator +
1387                                    artifact.getName() + ".java" );
1388 
1389                 if ( !source.getParentFile().exists()
1390                      && !source.getParentFile().mkdirs() )
1391                 {
1392                     throw new MojoExecutionException(
1393                         JavaContainerMojoBundle.getInstance().
1394                         getCannotCreateDirectoryMessage(
1395                         Locale.getDefault(), source.getParentFile().
1396                         getAbsolutePath() ) );
1397 
1398                 }
1399 
1400                 if ( this.getEncoding() == null )
1401                 {
1402                     writer = new FileWriter( source );
1403                 }
1404                 else
1405                 {
1406                     writer = new OutputStreamWriter(
1407                         new FileOutputStream( source ), this.getEncoding() );
1408 
1409                 }
1410 
1411                 final VelocityContext ctx = new VelocityContext();
1412                 ctx.put( "artifact", artifact );
1413                 ctx.put( "project", this.getMavenProject() );
1414                 ctx.put( "implementation", impl );
1415                 ctx.put( "constructorsStartingMarker",
1416                          this.constructorsStartingMarker );
1417 
1418                 ctx.put( "constructorsEndingMarker",
1419                          this.constructorsEndingMarker );
1420 
1421                 ctx.put( "dependenciesStartingMarker",
1422                          this.dependenciesStartingMarker );
1423 
1424                 ctx.put( "dependenciesEndingMarker",
1425                          this.dependenciesEndingMarker );
1426 
1427                 ctx.put( "propertiesStartingMarker",
1428                          this.propertiesStartingMarker );
1429 
1430                 ctx.put( "propertiesEndingMarker",
1431                          this.propertiesEndingMarker );
1432 
1433                 ctx.put( "messagesStartingMarker",
1434                          this.messagesStartingMarker );
1435 
1436                 ctx.put( "messagesEndingMarker",
1437                          this.messagesEndingMarker );
1438 
1439                 this.getVelocity().mergeTemplate(
1440                     IMPLEMENTATION_TEMPLATE_LOCATION, "UTF-8", ctx, writer );
1441 
1442                 writer.close();
1443                 writer = null;
1444 
1445                 this.getLog().info( JavaContainerMojoBundle.getInstance().
1446                     getCreatedFileMessage( Locale.getDefault(),
1447                                            source.getName() ) );
1448 
1449             }
1450 
1451             final String content = this.load( source );
1452             final String path = source.getAbsolutePath();
1453 
1454             final DependencyEditor depEditor =
1455                 new DependencyEditor( path, impl );
1456 
1457             final PropertyEditor propEditor =
1458                 new PropertyEditor( path, impl );
1459 
1460             final ConstructorsEditor ctorsEditor =
1461                 new ConstructorsEditor( path, impl );
1462 
1463             final MessageEditor messageEditor = new MessageEditor( path, impl );
1464 
1465             edited =
1466                 this.edit( content,
1467                            new RemovingEditor( path,
1468                                                this.implementationsStartingMarker,
1469                                                this.implementationsEndingMarker ) );
1470 
1471             edited =
1472                 this.edit( edited,
1473                            new RemovingEditor( path,
1474                                                this.dependenciesStartingMarker,
1475                                                this.dependenciesEndingMarker ) );
1476 
1477             edited =
1478                 this.edit( edited,
1479                            new RemovingEditor( path,
1480                                                this.propertiesStartingMarker,
1481                                                this.propertiesEndingMarker ) );
1482 
1483             edited =
1484                 this.edit( edited,
1485                            new RemovingEditor( path,
1486                                                this.constructorsStartingMarker,
1487                                                this.constructorsEndingMarker ) );
1488 
1489             edited =
1490                 this.edit( edited,
1491                            new RemovingEditor( path,
1492                                                this.messagesStartingMarker,
1493                                                this.messagesEndingMarker ) );
1494 
1495             edited = this.edit( edited, depEditor );
1496             edited = this.edit( edited, propEditor );
1497             edited = this.edit( edited, ctorsEditor );
1498             edited = this.edit( edited, messageEditor );
1499             edited = this.edit( edited,
1500                                 new CleanMojo.RemoveTrailingSpacesEditor() );
1501 
1502             if ( depEditor.isMarkersNeeded() && !depEditor.isModified() )
1503             {
1504                 throw new MojoExecutionException(
1505                     JavaContainerMojoBundle.getInstance().
1506                     getMissingMarkersMessage( Locale.getDefault(),
1507                                               this.dependenciesStartingMarker,
1508                                               path ) );
1509 
1510             }
1511 
1512             if ( propEditor.isMarkersNeeded() && !propEditor.isModified() )
1513             {
1514                 throw new MojoExecutionException(
1515                     JavaContainerMojoBundle.getInstance().
1516                     getMissingMarkersMessage( Locale.getDefault(),
1517                                               this.propertiesStartingMarker,
1518                                               path ) );
1519 
1520             }
1521 
1522             if ( ctorsEditor.isMarkersNeeded() && !ctorsEditor.isModified() )
1523             {
1524                 throw new MojoExecutionException(
1525                     JavaContainerMojoBundle.getInstance().
1526                     getMissingMarkersMessage( Locale.getDefault(),
1527                                               this.constructorsStartingMarker,
1528                                               path ) );
1529 
1530             }
1531 
1532             if ( messageEditor.isMarkersNeeded() &&
1533                  !messageEditor.isModified() )
1534             {
1535                 throw new MojoExecutionException(
1536                     JavaContainerMojoBundle.getInstance().
1537                     getMissingMarkersMessage( Locale.getDefault(),
1538                                               this.messagesStartingMarker,
1539                                               path ) );
1540 
1541             }
1542 
1543             if ( !content.equals( edited ) )
1544             {
1545                 this.save( source, edited );
1546             }
1547         }
1548         catch ( final Exception e )
1549         {
1550             throw (MojoFailureException) new MojoFailureException(
1551                 e.getMessage() ).initCause( e );
1552 
1553         }
1554         finally
1555         {
1556             try
1557             {
1558                 if ( writer != null )
1559                 {
1560                     writer.close();
1561                 }
1562             }
1563             catch ( final IOException e )
1564             {
1565                 this.getLog().error( e );
1566             }
1567         }
1568     }
1569 
1570     protected void generateSpecification( final List roots,
1571                                           final Specification spec )
1572         throws MojoExecutionException, MojoFailureException, IOException
1573     {
1574         if ( spec == null )
1575         {
1576             throw new NullPointerException( "spec" );
1577         }
1578 
1579         if ( roots == null )
1580         {
1581             throw new NullPointerException( "roots" );
1582         }
1583 
1584         final File source = this.getSource( roots, spec.getIdentifier() );
1585         if ( source != null )
1586         {
1587             final String path = source.getAbsolutePath();
1588             final String content = this.load( source );
1589             String edited = this.edit( content, new RemovingEditor(
1590                 path, this.specificationsStartingMarker,
1591                 this.specificationsEndingMarker ) );
1592 
1593             edited = this.edit( edited,
1594                                 new CleanMojo.RemoveTrailingSpacesEditor() );
1595 
1596             if ( !content.equals( edited ) )
1597             {
1598                 this.save( source, edited );
1599             }
1600 
1601         }
1602     }
1603 
1604     private String getOpeningFoldingMarker( final String section )
1605     {
1606         final StringBuffer buf = new StringBuffer( 500 );
1607         if ( this.getTargetEditor().equals( "netbeans" ) )
1608         {
1609             buf.append( JavaContainerMojoBundle.getInstance().
1610                 getOpeningFoldingMarkerNetbeansMessage(
1611                 this.getLocale(), section ) );
1612 
1613         }
1614 
1615         return buf.toString();
1616     }
1617 
1618     private String getClosingFoldingMarker( final String section )
1619     {
1620         final StringBuffer buf = new StringBuffer( 500 );
1621         if ( this.getTargetEditor().equals( "netbeans" ) )
1622         {
1623             buf.append( JavaContainerMojoBundle.getInstance().
1624                 getClosingFoldingMarkerNetbeansMessage(
1625                 this.getLocale(), section ) );
1626 
1627         }
1628 
1629         return buf.toString();
1630     }
1631 
1632     protected void writeContainerReport( final Model model,
1633                                          final String name )
1634         throws JAXBException, IOException, MojoExecutionException
1635     {
1636         final File reportFile = new File( this.getOutputDirectory(),
1637                                           name );
1638 
1639         if ( !reportFile.getParentFile().exists()
1640              && !reportFile.getParentFile().mkdirs() )
1641         {
1642             throw new MojoExecutionException(
1643                 JavaContainerMojoBundle.getInstance().
1644                 getCannotCreateDirectoryMessage(
1645                 Locale.getDefault(), reportFile.getParentFile().
1646                 getAbsolutePath() ) );
1647 
1648         }
1649 
1650         OutputStream out = null;
1651 
1652         try
1653         {
1654             out = new FileOutputStream( reportFile );
1655             this.getModelManager().getContainerMarshaller().marshal(
1656                 this.getModelManager().getContainerModel( model.getModules() ),
1657                 out );
1658 
1659             out.close();
1660             out = null;
1661         }
1662         finally
1663         {
1664             try
1665             {
1666                 if ( out != null )
1667                 {
1668                     out.close();
1669                 }
1670             }
1671             catch ( final IOException e )
1672             {
1673                 this.getLog().error( e );
1674             }
1675         }
1676     }
1677 
1678     //-----------------------------------------------------------ContainerMojo--
1679 }