001    /*
002     *   Copyright (c) 2009 The JOMC Project
003     *   Copyright (c) 2005 Christian Schulte <cs@jomc.org>
004     *   All rights reserved.
005     *
006     *   Redistribution and use in source and binary forms, with or without
007     *   modification, are permitted provided that the following conditions
008     *   are met:
009     *
010     *     o Redistributions of source code must retain the above copyright
011     *       notice, this list of conditions and the following disclaimer.
012     *
013     *     o Redistributions in binary form must reproduce the above copyright
014     *       notice, this list of conditions and the following disclaimer in
015     *       the documentation and/or other materials provided with the
016     *       distribution.
017     *
018     *   THIS SOFTWARE IS PROVIDED BY THE JOMC PROJECT AND CONTRIBUTORS "AS IS"
019     *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020     *   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021     *   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JOMC PROJECT OR
022     *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023     *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024     *   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025     *   OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026     *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
027     *   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
028     *   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029     *
030     *   $Id: JavaSources.java 891 2009-11-02 03:40:00Z schulte2005 $
031     *
032     */
033    package org.jomc.tools;
034    
035    import java.io.File;
036    import java.io.IOException;
037    import java.io.StringWriter;
038    import java.text.MessageFormat;
039    import java.util.ResourceBundle;
040    import java.util.logging.Level;
041    import org.apache.commons.io.FileUtils;
042    import org.apache.velocity.Template;
043    import org.apache.velocity.VelocityContext;
044    import org.jomc.model.Dependencies;
045    import org.jomc.model.Implementation;
046    import org.jomc.model.Messages;
047    import org.jomc.model.Module;
048    import org.jomc.model.Properties;
049    import org.jomc.model.Specification;
050    import org.jomc.model.Specifications;
051    import org.jomc.util.LineEditor;
052    import org.jomc.util.Section;
053    import org.jomc.util.SectionEditor;
054    import org.jomc.util.TrailingWhitespaceEditor;
055    
056    /**
057     * Manages Java source code.
058     *
059     * <p><b>Use cases</b><br/><ul>
060     * <li>{@link #manageSources(java.io.File) }</li>
061     * <li>{@link #manageSources(org.jomc.model.Module, java.io.File) }</li>
062     * <li>{@link #manageSources(org.jomc.model.Specification, java.io.File) }</li>
063     * <li>{@link #manageSources(org.jomc.model.Implementation, java.io.File) }</li>
064     * </ul></p>
065     *
066     * @author <a href="mailto:cs@jomc.org">Christian Schulte</a>
067     * @version $Id: JavaSources.java 891 2009-11-02 03:40:00Z schulte2005 $
068     *
069     * @see #getModules()
070     */
071    public class JavaSources extends JomcTool
072    {
073    
074        /** Constant for the name of the constructors source code section. */
075        private static final String CONSTRUCTORS_SECTION_NAME = "Constructors";
076    
077        /** Constant for the name of the default constructor source code section. */
078        private static final String DEFAULT_CONSTRUCTOR_SECTION_NAME = "Default Constructor";
079    
080        /** Constant for the name of the dependencies source code section. */
081        private static final String DEPENDENCIES_SECTION_NAME = "Dependencies";
082    
083        /** Constant for the name of the properties source code section. */
084        private static final String PROPERTIES_SECTION_NAME = "Properties";
085    
086        /** Constant for the name of the messages source code section. */
087        private static final String MESSAGES_SECTION_NAME = "Messages";
088    
089        /** Constant for the name of the license source code section. */
090        private static final String LICENSE_SECTION_NAME = "License Header";
091    
092        /** Constant for the name of the documentation source code section. */
093        private static final String DOCUMENTATION_SECTION_NAME = "Documentation";
094    
095        /** Constant for the name of the implementation annotations source code section. */
096        private static final String ANNOTATIONS_SECTION_NAME = "Annotations";
097    
098        /** Name of the generator. */
099        private static final String GENERATOR_NAME = JavaSources.class.getName();
100    
101        /** Constant for the version of the generator. */
102        private static final String GENERATOR_VERSION = "1.0";
103    
104        /** Name of the {@code implementation-constructors-head.vm} template. */
105        private static final String CONSTRUCTORS_HEAD_TEMPLATE = "implementation-constructors-head.vm";
106    
107        /** Name of the {@code implementation-constructors-tail.vm} template. */
108        private static final String CONSTRUCTORS_TAIL_TEMPLATE = "implementation-constructors-tail.vm";
109    
110        /** Name of the {@code implementation-dependencies.vm} template. */
111        private static final String DEPENDENCIES_TEMPLATE = "implementation-dependencies.vm";
112    
113        /** Name of the {@code implementation-properties.vm} template. */
114        private static final String PROPERTIES_TEMPLATE = "implementation-properties.vm";
115    
116        /** Name of the {@code implementation-messages.vm} template. */
117        private static final String MESSAGES_TEMPLATE = "implementation-messages.vm";
118    
119        /** Name of the {@code specification-license.vm} template. */
120        private static final String SPECIFICATION_LICENSE_TEMPLATE = "specification-license.vm";
121    
122        /** Name of the {@code implementation-license.vm} template. */
123        private static final String IMPLEMENTATION_LICENSE_TEMPLATE = "implementation-license.vm";
124    
125        /** Name of the {@code specification-documentation.vm} template. */
126        private static final String SPECIFICATION_DOCUMENTATION_TEMPLATE = "specification-documentation.vm";
127    
128        /** Name of the {@code implementation-documentation.vm} template. */
129        private static final String IMPLEMENTATION_DOCUMENTATION_TEMPLATE = "implementation-documentation.vm";
130    
131        /** Name of the {@code Implementation.java.vm} template. */
132        private static final String IMPLEMENTATION_TEMPLATE = "Implementation.java.vm";
133    
134        /** Name of the {@code Specification.java.vm} template. */
135        private static final String SPECIFICATION_TEMPLATE = "Specification.java.vm";
136    
137        /** Name of the {@code specification-annotations.vm} template. */
138        private static final String SPECIFICATION_ANNOTATIONS_TEMPLATE = "specification-annotations.vm";
139    
140        /** Name of the {@code implementation-annotations.vm} template. */
141        private static final String IMPLEMENTATION_ANNOTATIONS_TEMPLATE = "implementation-annotations.vm";
142    
143        /** Creates a new {@code JavaSources} instance. */
144        public JavaSources()
145        {
146            super();
147        }
148    
149        /**
150         * Creates a new {@code JavaSources} instance taking a {@code JavaSources} instance to initialize the instance with.
151         *
152         * @param tool The instance to initialize the new instance with,
153         */
154        public JavaSources( final JavaSources tool )
155        {
156            super( tool );
157        }
158    
159        /**
160         * Manages the source code of the modules of the instance.
161         *
162         * @param sourcesDirectory The directory holding the sources to manage.
163         *
164         * @throws NullPointerException if {@code sourcesDirectory} is {@code null}.
165         * @throws IOException if managing sources fails.
166         *
167         * @see #manageSources(org.jomc.model.Module, java.io.File)
168         */
169        public void manageSources( final File sourcesDirectory ) throws IOException
170        {
171            if ( sourcesDirectory == null )
172            {
173                throw new NullPointerException( "sourcesDirectory" );
174            }
175    
176            for ( Module m : this.getModules().getModule() )
177            {
178                this.manageSources( m, sourcesDirectory );
179            }
180        }
181    
182        /**
183         * Manages the source code of a given module of the modules of the instance.
184         *
185         * @param module The module to process.
186         * @param sourcesDirectory The directory holding the sources to manage.
187         *
188         * @throws NullPointerException if {@code module} or {@code sourcesDirectory} is {@code null}.
189         * @throws IOException if managing sources fails.
190         *
191         * @see #manageSources(org.jomc.model.Specification, java.io.File)
192         * @see #manageSources(org.jomc.model.Implementation, java.io.File)
193         */
194        public void manageSources( final Module module, final File sourcesDirectory ) throws IOException
195        {
196            if ( module == null )
197            {
198                throw new NullPointerException( "module" );
199            }
200            if ( sourcesDirectory == null )
201            {
202                throw new NullPointerException( "sourcesDirectory" );
203            }
204    
205            if ( module.getSpecifications() != null )
206            {
207                for ( Specification s : module.getSpecifications().getSpecification() )
208                {
209                    this.manageSources( s, sourcesDirectory );
210                }
211            }
212            if ( module.getImplementations() != null )
213            {
214                for ( Implementation i : module.getImplementations().getImplementation() )
215                {
216                    this.manageSources( i, sourcesDirectory );
217                }
218            }
219        }
220    
221        /**
222         * Manages the source code of a given specification of the modules of the instance.
223         *
224         * @param specification The specification to process.
225         * @param sourcesDirectory The directory holding the sources to manage.
226         *
227         * @throws NullPointerException if {@code specification} or {@code sourcesDirectory} is {@code null}.
228         * @throws IOException if managing sources fails.
229         *
230         * @see #getSpecificationEditor(org.jomc.model.Specification)
231         */
232        public void manageSources( final Specification specification, final File sourcesDirectory ) throws IOException
233        {
234            if ( specification == null )
235            {
236                throw new NullPointerException( "specification" );
237            }
238            if ( sourcesDirectory == null )
239            {
240                throw new NullPointerException( "sourcesDirectory" );
241            }
242    
243            final Implementation i = this.getModules().getImplementation( specification.getIdentifier() );
244            if ( i != null && this.isJavaClassDeclaration( i ) )
245            {
246                this.manageSources( i, sourcesDirectory );
247            }
248            else if ( this.isJavaClassDeclaration( specification ) )
249            {
250                final File f = new File( sourcesDirectory, specification.getIdentifier().replace( '.', '/' ) + ".java" );
251                final String content = f.exists()
252                                       ? FileUtils.readFileToString( f, this.getInputEncoding() )
253                                       : this.getSpecificationTemplate( specification );
254    
255                final JavaSpecificationEditor editor = this.getSpecificationEditor( specification );
256                final String edited;
257                try
258                {
259                    edited = editor.edit( content );
260                }
261                catch ( final IOException e )
262                {
263                    throw (IOException) new IOException( this.getMessage( "failedEditing", new Object[]
264                        {
265                            f.getCanonicalPath(), e.getMessage()
266                        } ) ).initCause( e );
267    
268                }
269    
270                if ( !editor.isLicenseSectionPresent() && this.isLoggable( Level.INFO ) )
271                {
272                    this.log( Level.INFO, this.getMessage( "missingOptionalSection", new Object[]
273                        {
274                            LICENSE_SECTION_NAME,
275                            f.getCanonicalPath()
276                        } ), null );
277    
278                }
279    
280                if ( !editor.isAnnotationsSectionPresent() )
281                {
282                    throw new IOException( this.getMessage( "missingSection", new Object[]
283                        {
284                            ANNOTATIONS_SECTION_NAME,
285                            f.getCanonicalPath()
286                        } ) );
287    
288                }
289    
290                if ( !editor.isDocumentationSectionPresent() && this.isLoggable( Level.INFO ) )
291                {
292                    this.log( Level.INFO, this.getMessage( "missingOptionalSection", new Object[]
293                        {
294                            DOCUMENTATION_SECTION_NAME,
295                            f.getCanonicalPath()
296                        } ), null );
297    
298                }
299    
300                if ( !edited.equals( content ) )
301                {
302                    if ( !f.getParentFile().exists() && !f.getParentFile().mkdirs() )
303                    {
304                        throw new IOException( this.getMessage( "failedCreatingDirectory", new Object[]
305                            {
306                                f.getParentFile().getAbsolutePath()
307                            } ) );
308    
309                    }
310    
311                    if ( this.isLoggable( Level.INFO ) )
312                    {
313                        this.log( Level.INFO, this.getMessage( "editing", new Object[]
314                            {
315                                f.getCanonicalPath()
316                            } ), null );
317    
318                    }
319    
320                    FileUtils.writeStringToFile( f, edited, this.getOutputEncoding() );
321                }
322            }
323        }
324    
325        /**
326         * Manages the source code of a given implementation of the modules of the instance.
327         *
328         * @param implementation The implementation to process.
329         * @param sourcesDirectory The directory holding the sources to manage.
330         *
331         * @throws NullPointerException if {@code implementation} or {@code sourcesDirectory} is {@code null}.
332         * @throws IOException if managing sources fails.
333         *
334         * @see #getImplementationEditor(org.jomc.model.Implementation)
335         */
336        public void manageSources( final Implementation implementation, final File sourcesDirectory ) throws IOException
337        {
338            if ( implementation == null )
339            {
340                throw new NullPointerException( "implementation" );
341            }
342            if ( sourcesDirectory == null )
343            {
344                throw new NullPointerException( "sourcesDirectory" );
345            }
346    
347            if ( this.isJavaClassDeclaration( implementation ) )
348            {
349                final File f = new File( sourcesDirectory, implementation.getClazz().replace( '.', '/' ) + ".java" );
350                final String content = f.exists()
351                                       ? FileUtils.readFileToString( f, this.getInputEncoding() )
352                                       : this.getImplementationTemplate( implementation );
353    
354                final JavaImplementationEditor editor = this.getImplementationEditor( implementation );
355                final String edited;
356                try
357                {
358                    edited = editor.edit( content );
359                }
360                catch ( final IOException e )
361                {
362                    throw (IOException) new IOException( this.getMessage( "failedEditing", new Object[]
363                        {
364                            f.getCanonicalPath(), e.getMessage()
365                        } ) ).initCause( e );
366    
367                }
368    
369                if ( !editor.isLicenseSectionPresent() && this.isLoggable( Level.INFO ) )
370                {
371                    this.log( Level.INFO, this.getMessage( "missingOptionalSection", new Object[]
372                        {
373                            LICENSE_SECTION_NAME,
374                            f.getCanonicalPath()
375                        } ), null );
376    
377                }
378    
379                if ( !editor.isAnnotationsSectionPresent() )
380                {
381                    throw new IOException( this.getMessage( "missingSection", new Object[]
382                        {
383                            ANNOTATIONS_SECTION_NAME,
384                            f.getCanonicalPath()
385                        } ) );
386    
387                }
388    
389                if ( !editor.isDocumentationSectionPresent() && this.isLoggable( Level.INFO ) )
390                {
391                    this.log( Level.INFO, this.getMessage( "missingOptionalSection", new Object[]
392                        {
393                            DOCUMENTATION_SECTION_NAME,
394                            f.getCanonicalPath()
395                        } ), null );
396    
397                }
398    
399                if ( !editor.isConstructorsSectionPresent() )
400                {
401                    final Specifications specifications =
402                        this.getModules().getSpecifications( implementation.getIdentifier() );
403    
404                    if ( specifications != null &&
405                         !( specifications.getSpecification().isEmpty() && specifications.getReference().isEmpty() ) )
406                    {
407                        throw new IOException( this.getMessage( "missingSection", new Object[]
408                            {
409                                CONSTRUCTORS_SECTION_NAME,
410                                f.getCanonicalPath()
411                            } ) );
412    
413                    }
414                    else if ( this.isLoggable( Level.INFO ) )
415                    {
416                        this.log( Level.INFO, this.getMessage( "missingOptionalSection", new Object[]
417                            {
418                                CONSTRUCTORS_SECTION_NAME,
419                                f.getCanonicalPath()
420                            } ), null );
421    
422                    }
423                }
424                else if ( !editor.isDefaultConstructorSectionPresent() )
425                {
426                    throw new IOException( this.getMessage( "missingSection", new Object[]
427                        {
428                            DEFAULT_CONSTRUCTOR_SECTION_NAME,
429                            f.getCanonicalPath()
430                        } ) );
431    
432                }
433    
434                if ( !editor.isPropertiesSectionPresent() )
435                {
436                    final Properties properties = this.getModules().getProperties( implementation.getIdentifier() );
437    
438                    if ( properties != null && !properties.getProperty().isEmpty() )
439                    {
440                        throw new IOException( this.getMessage( "missingSection", new Object[]
441                            {
442                                PROPERTIES_SECTION_NAME,
443                                f.getCanonicalPath()
444                            } ) );
445    
446                    }
447                    else if ( this.isLoggable( Level.INFO ) )
448                    {
449                        this.log( Level.INFO, this.getMessage( "missingOptionalSection", new Object[]
450                            {
451                                PROPERTIES_SECTION_NAME,
452                                f.getCanonicalPath()
453                            } ), null );
454    
455                    }
456                }
457    
458                if ( !editor.isDependenciesSectionPresent() )
459                {
460                    final Dependencies dependencies = this.getModules().getDependencies( implementation.getIdentifier() );
461    
462                    if ( dependencies != null && !dependencies.getDependency().isEmpty() )
463                    {
464                        throw new IOException( this.getMessage( "missingSection", new Object[]
465                            {
466                                DEPENDENCIES_SECTION_NAME,
467                                f.getCanonicalPath()
468                            } ) );
469    
470                    }
471                    else if ( this.isLoggable( Level.INFO ) )
472                    {
473                        this.log( Level.INFO, this.getMessage( "missingOptionalSection", new Object[]
474                            {
475                                DEPENDENCIES_SECTION_NAME,
476                                f.getCanonicalPath()
477                            } ), null );
478    
479                    }
480                }
481    
482                if ( !editor.isMessagesSectionPresent() )
483                {
484                    final Messages messages = this.getModules().getMessages( implementation.getIdentifier() );
485    
486                    if ( messages != null && !messages.getMessage().isEmpty() )
487                    {
488                        throw new IOException( this.getMessage( "missingSection", new Object[]
489                            {
490                                MESSAGES_SECTION_NAME,
491                                f.getCanonicalPath()
492                            } ) );
493    
494                    }
495                    else if ( this.isLoggable( Level.INFO ) )
496                    {
497                        this.log( Level.INFO, this.getMessage( "missingOptionalSection", new Object[]
498                            {
499                                MESSAGES_SECTION_NAME,
500                                f.getCanonicalPath()
501                            } ), null );
502    
503                    }
504                }
505    
506                if ( !edited.equals( content ) )
507                {
508                    if ( !f.getParentFile().exists() && !f.getParentFile().mkdirs() )
509                    {
510                        throw new IOException( this.getMessage( "failedCreatingDirectory", new Object[]
511                            {
512                                f.getParentFile().getAbsolutePath()
513                            } ) );
514    
515                    }
516    
517                    if ( this.isLoggable( Level.INFO ) )
518                    {
519                        this.log( Level.INFO, this.getMessage( "editing", new Object[]
520                            {
521                                f.getCanonicalPath()
522                            } ), null );
523    
524                    }
525    
526                    FileUtils.writeStringToFile( f, edited, this.getOutputEncoding() );
527                }
528            }
529        }
530    
531        /**
532         * Gets a new editor for editing Java specification source code.
533         *
534         * @param specification The specification to create a new editor for.
535         *
536         * @return A new editor for editing the source code of {@code specification}.
537         *
538         * @throws NullPointerException if {@code specification} is {@code null}.
539         */
540        public JavaSpecificationEditor getSpecificationEditor( final Specification specification )
541        {
542            if ( specification == null )
543            {
544                throw new NullPointerException( "specification" );
545            }
546    
547            return new JavaSpecificationEditor( new TrailingWhitespaceEditor(), specification );
548        }
549    
550        /**
551         * Gets a new editor for editing Java implementation source code.
552         *
553         * @param implementation The implementation to create a new editor for.
554         *
555         * @return A new editor for editing the source code of {@code implementation}.
556         *
557         * @throws NullPointerException if {@code implementation} is {@code null}.
558         */
559        public JavaImplementationEditor getImplementationEditor( final Implementation implementation )
560        {
561            if ( implementation == null )
562            {
563                throw new NullPointerException( "implementation" );
564            }
565    
566            return new JavaImplementationEditor( new TrailingWhitespaceEditor(), implementation );
567        }
568    
569        /**
570         * Gets the velocity context used for merging templates.
571         *
572         * @return The velocity context used for merging templates.
573         */
574        @Override
575        public VelocityContext getVelocityContext()
576        {
577            final VelocityContext ctx = super.getVelocityContext();
578            ctx.put( "generatorName", GENERATOR_NAME );
579            ctx.put( "generatorVersion", GENERATOR_VERSION );
580            return ctx;
581        }
582    
583        /**
584         * Gets the Java source code template of specification.
585         *
586         * @param specification The specification to get the source code template of.
587         *
588         * @throws IOException if getting the source code section fails.
589         */
590        private String getSpecificationTemplate( final Specification specification ) throws IOException
591        {
592            final StringWriter writer = new StringWriter();
593            final VelocityContext ctx = this.getVelocityContext();
594            final Template template = this.getVelocityTemplate( SPECIFICATION_TEMPLATE );
595            ctx.put( "specification", specification );
596            ctx.put( "template", template );
597            template.merge( ctx, writer );
598            writer.close();
599            return writer.toString();
600        }
601    
602        /**
603         * Gets the Java source code template of an implementation.
604         *
605         * @param implementation The implementation to get the source code template of.
606         *
607         * @throws IOException if getting the source code section fails.
608         */
609        private String getImplementationTemplate( final Implementation implementation ) throws IOException
610        {
611            final StringWriter writer = new StringWriter();
612            final VelocityContext ctx = this.getVelocityContext();
613            final Template template = this.getVelocityTemplate( IMPLEMENTATION_TEMPLATE );
614            ctx.put( "implementation", implementation );
615            ctx.put( "template", template );
616            template.merge( ctx, writer );
617            writer.close();
618            return writer.toString();
619        }
620    
621        /**
622         * Gets the Java source code of the license section of a specification.
623         *
624         * @param specification The specification to get the source code of the license section of.
625         *
626         * @throws IOException if getting the source code section fails.
627         */
628        private String getLicenseSection( final Specification specification ) throws IOException
629        {
630            final StringWriter writer = new StringWriter();
631            final VelocityContext ctx = this.getVelocityContext();
632            final Template template = this.getVelocityTemplate( SPECIFICATION_LICENSE_TEMPLATE );
633            ctx.put( "specification", specification );
634            ctx.put( "template", template );
635            template.merge( ctx, writer );
636            writer.close();
637            return writer.toString();
638        }
639    
640        /**
641         * Gets the Java source code of the license section of an implementation..
642         *
643         * @param implementation The implementation to get the source code of the license section of.
644         *
645         * @throws IOException if getting the source code section fails.
646         */
647        private String getLicenseSection( final Implementation implementation ) throws IOException
648        {
649            final StringWriter writer = new StringWriter();
650            final VelocityContext ctx = this.getVelocityContext();
651            final Template template = this.getVelocityTemplate( IMPLEMENTATION_LICENSE_TEMPLATE );
652            ctx.put( "implementation", implementation );
653            ctx.put( "template", template );
654            template.merge( ctx, writer );
655            writer.close();
656            return writer.toString();
657        }
658    
659        /**
660         * Gets the Java source code of the specification annotations section.
661         *
662         * @param specification The specification to get the source code of the annotations section of.
663         *
664         * @throws IOException if getting the source code section fails.
665         */
666        private String getAnnotationsSection( final Specification specification ) throws IOException
667        {
668            final StringWriter writer = new StringWriter();
669            final VelocityContext ctx = this.getVelocityContext();
670            final Template template = this.getVelocityTemplate( SPECIFICATION_ANNOTATIONS_TEMPLATE );
671            ctx.put( "specification", specification );
672            ctx.put( "template", template );
673            template.merge( ctx, writer );
674            writer.close();
675            return writer.toString();
676        }
677    
678        /**
679         * Gets the Java source code of the implementation annotations section.
680         *
681         * @param implementation The implementation to get the source code of the annotations section of.
682         *
683         * @throws IOException if getting the source code section fails.
684         */
685        private String getAnnotationsSection( final Implementation implementation ) throws IOException
686        {
687            final StringWriter writer = new StringWriter();
688            final VelocityContext ctx = this.getVelocityContext();
689            final Template template = this.getVelocityTemplate( IMPLEMENTATION_ANNOTATIONS_TEMPLATE );
690            ctx.put( "implementation", implementation );
691            ctx.put( "template", template );
692            template.merge( ctx, writer );
693            writer.close();
694            return writer.toString();
695        }
696    
697        /**
698         * Gets the Java source code of the documentation section of a specification.
699         *
700         * @param specification The specification to get the source code section of.
701         *
702         * @throws IOException if getting the source code section fails.
703         */
704        private String getDocumentationSection( final Specification specification ) throws IOException
705        {
706            final StringWriter writer = new StringWriter();
707            final VelocityContext ctx = this.getVelocityContext();
708            final Template template = this.getVelocityTemplate( SPECIFICATION_DOCUMENTATION_TEMPLATE );
709            ctx.put( "specification", specification );
710            ctx.put( "template", template );
711            template.merge( ctx, writer );
712            writer.close();
713            return writer.toString();
714        }
715    
716        /**
717         * Gets the Java source code of the documentation section of an implementation.
718         *
719         * @param implementation The implementation to get the source code section of.
720         *
721         * @throws IOException if getting the source code section fails.
722         */
723        private String getDocumentationSection( final Implementation implementation ) throws IOException
724        {
725            final StringWriter writer = new StringWriter();
726            final VelocityContext ctx = this.getVelocityContext();
727            final Template template = this.getVelocityTemplate( IMPLEMENTATION_DOCUMENTATION_TEMPLATE );
728            ctx.put( "implementation", implementation );
729            ctx.put( "template", template );
730            template.merge( ctx, writer );
731            writer.close();
732            return writer.toString();
733        }
734    
735        /**
736         * Gets the Java source code of the constructors section head content of an implementation.
737         *
738         * @param implementation The implementation to get the constructors section head content of.
739         *
740         * @throws IOException if getting the source code section fails.
741         */
742        private String getConstructorsSectionHeadContent( final Implementation implementation ) throws IOException
743        {
744            final StringWriter writer = new StringWriter();
745            final VelocityContext ctx = this.getVelocityContext();
746            final Template template = this.getVelocityTemplate( CONSTRUCTORS_HEAD_TEMPLATE );
747            ctx.put( "implementation", implementation );
748            ctx.put( "template", template );
749            template.merge( ctx, writer );
750            writer.close();
751            return writer.toString();
752        }
753    
754        /**
755         * Gets the Java source code of the constructors section tail content of an implementation.
756         *
757         * @param implementation The implementation to get the constructors section tail content of.
758         *
759         * @throws IOException if getting the source code section fails.
760         */
761        private String getConstructorsSectionTailContent( final Implementation implementation ) throws IOException
762        {
763            final StringWriter writer = new StringWriter();
764            final VelocityContext ctx = this.getVelocityContext();
765            final Template template = this.getVelocityTemplate( CONSTRUCTORS_TAIL_TEMPLATE );
766            ctx.put( "implementation", implementation );
767            ctx.put( "template", template );
768            template.merge( ctx, writer );
769            writer.close();
770            return writer.toString();
771        }
772    
773        /**
774         * Gets the Java source code of the dependencies section of an implementation.
775         *
776         * @param implementation The implementation to get the source code of the dependencies section of.
777         *
778         * @throws IOException if getting the source code section fails.
779         */
780        private String getDependenciesSection( final Implementation implementation ) throws IOException
781        {
782            final StringWriter writer = new StringWriter();
783            final VelocityContext ctx = this.getVelocityContext();
784            final Template template = this.getVelocityTemplate( DEPENDENCIES_TEMPLATE );
785            ctx.put( "implementation", implementation );
786            ctx.put( "template", template );
787            template.merge( ctx, writer );
788            writer.close();
789            return writer.toString();
790        }
791    
792        /**
793         * Gets the Java source code of the properties section of an implementation.
794         *
795         * @param implementation The implementation to get the source code of the properties section of.
796         *
797         * @throws IOException if getting the source code section fails.
798         */
799        private String getPropertiesSection( final Implementation implementation ) throws IOException
800        {
801            final StringWriter writer = new StringWriter();
802            final VelocityContext ctx = this.getVelocityContext();
803            final Template template = this.getVelocityTemplate( PROPERTIES_TEMPLATE );
804            ctx.put( "implementation", implementation );
805            ctx.put( "template", template );
806            template.merge( ctx, writer );
807            writer.close();
808            return writer.toString();
809        }
810    
811        /**
812         * Gets the Java source code of the messages section of an implementation.
813         *
814         * @param implementation The implementation to get the source code of the messages section of.
815         *
816         * @throws IOException if getting the source code section fails.
817         */
818        private String getMessagesSection( final Implementation implementation ) throws IOException
819        {
820            final StringWriter writer = new StringWriter();
821            final VelocityContext ctx = this.getVelocityContext();
822            final Template template = this.getVelocityTemplate( MESSAGES_TEMPLATE );
823            ctx.put( "implementation", implementation );
824            ctx.put( "template", template );
825            template.merge( ctx, writer );
826            writer.close();
827            return writer.toString();
828        }
829    
830        private String getMessage( final String key, final Object args )
831        {
832            final ResourceBundle b = ResourceBundle.getBundle( JavaSources.class.getName().replace( '.', '/' ) );
833            final MessageFormat f = new MessageFormat( b.getString( key ) );
834            return f.format( args );
835        }
836    
837        /**
838         * Extension to {@code SectionEditor} for editing Java source code.
839         *
840         * @author <a href="mailto:cs@jomc.org">Christian Schulte</a>
841         * @version $Id: JavaSources.java 891 2009-11-02 03:40:00Z schulte2005 $
842         */
843        public abstract class JavaEditor extends SectionEditor
844        {
845    
846            /** Flag indicating that the source code of the editor contains a license section. */
847            private boolean licenseSectionPresent;
848    
849            /** Flag indicating that the source code of the editor contains an annotations section. */
850            private boolean annotationsSectionPresent;
851    
852            /** Flag indicating that the source code of the editor contains a documentation section. */
853            private boolean documentationSectionPresent;
854    
855            /** Creates a new {@code JavaEditor} instance. */
856            public JavaEditor()
857            {
858                super();
859            }
860    
861            /**
862             * Creates a new {@code JavaEditor} instance taking a {@code LineEditor} to chain.
863             *
864             * @param lineEditor The editor to chain.
865             */
866            public JavaEditor( final LineEditor lineEditor )
867            {
868                super( lineEditor );
869            }
870    
871            @Override
872            public String getOutput( final Section section ) throws IOException
873            {
874                if ( section == null )
875                {
876                    throw new NullPointerException( "section" );
877                }
878    
879                this.licenseSectionPresent = false;
880                this.annotationsSectionPresent = false;
881                this.documentationSectionPresent = false;
882                return super.getOutput( section );
883            }
884    
885            @Override
886            public void editSection( final Section section ) throws IOException
887            {
888                if ( section == null )
889                {
890                    throw new NullPointerException( "section" );
891                }
892    
893                if ( section.getName() != null )
894                {
895                    if ( LICENSE_SECTION_NAME.equals( section.getName() ) )
896                    {
897                        this.editLicenseSection( section );
898                        this.licenseSectionPresent = true;
899                    }
900                    if ( ANNOTATIONS_SECTION_NAME.equals( section.getName() ) )
901                    {
902                        this.editAnnotationsSection( section );
903                        this.annotationsSectionPresent = true;
904                    }
905                    if ( DOCUMENTATION_SECTION_NAME.equals( section.getName() ) )
906                    {
907                        this.editDocumentationSection( section );
908                        this.documentationSectionPresent = true;
909                    }
910                }
911            }
912    
913            /**
914             * Edits the license section of the source code of the editor.
915             *
916             * @param s The section to edit.
917             *
918             * @throws NullPointerException if {@code s} is {@code null}.
919             * @throws IOException if editing {@code s} fails.
920             */
921            public abstract void editLicenseSection( final Section s ) throws IOException;
922    
923            /**
924             * Edits the annotations section of the source code of the editor.
925             *
926             * @param s The section to edit.
927             *
928             * @throws NullPointerException if {@code s} is {@code null}.
929             * @throws IOException if editing {@code s} fails.
930             */
931            public abstract void editAnnotationsSection( final Section s ) throws IOException;
932    
933            /**
934             * Edits the documentation section of the source code of the editor.
935             *
936             * @param s The section to edit.
937             *
938             * @throws NullPointerException if {@code s} is {@code null}.
939             * @throws IOException if editing {@code s} fails.
940             */
941            public abstract void editDocumentationSection( final Section s ) throws IOException;
942    
943            /**
944             * Gets a flag indicating that the source code of the editor contains a license section.
945             *
946             * @return {@code true} if the source code of the editor contains a license section; {@code false} if the
947             * source code of the editor does not contain a license section.
948             */
949            public boolean isLicenseSectionPresent()
950            {
951                return this.licenseSectionPresent;
952            }
953    
954            /**
955             * Gets a flag indicating that the source code of the editor contains an annotations section.
956             *
957             * @return {@code true} if the source code of the editor contains an annotations section; {@code false} if the
958             * source code of the editor does not contain an annotations section.
959             */
960            public boolean isAnnotationsSectionPresent()
961            {
962                return this.annotationsSectionPresent;
963            }
964    
965            /**
966             * Gets a flag indicating that the source code of the editor contains a documentation section.
967             *
968             * @return {@code true} if the source code of the editor contains a documentation section; {@code false} if the
969             * source code of the editor does not contain a documentation section.
970             */
971            public boolean isDocumentationSectionPresent()
972            {
973                return this.documentationSectionPresent;
974            }
975    
976        }
977    
978        /**
979         * Extension to {@code JavaEditor} for editing specification source code.
980         *
981         * @author <a href="mailto:cs@jomc.org">Christian Schulte</a>
982         * @version $Id: JavaSources.java 891 2009-11-02 03:40:00Z schulte2005 $
983         */
984        public class JavaSpecificationEditor extends JavaEditor
985        {
986    
987            /** The specification to edit. */
988            private Specification specification;
989    
990            /**
991             * Creates a new {@code JavaSpecificationEditor} instance for editing the source code of a given specification.
992             *
993             * @param specification The specification to edit.
994             */
995            public JavaSpecificationEditor( final Specification specification )
996            {
997                super();
998                this.specification = specification;
999            }
1000    
1001            /**
1002             * Creates a new {@code JavaSpecificationEditor} instance for editing the source code of a given specification
1003             * taking a {@code LineEditor} to chain.
1004             *
1005             * @param lineEditor The editor to chain.
1006             * @param specification The specification to edit.
1007             */
1008            public JavaSpecificationEditor( final LineEditor lineEditor, final Specification specification )
1009            {
1010                super( lineEditor );
1011                this.specification = specification;
1012            }
1013    
1014            /**
1015             * Edits the license section of the source code of the editor.
1016             *
1017             * @param s The section to edit.
1018             *
1019             * @throws NullPointerException if {@code s} is {@code null}.
1020             * @throws IOException if editing {@code s} fails.
1021             */
1022            public void editLicenseSection( final Section s ) throws IOException
1023            {
1024                if ( s == null )
1025                {
1026                    throw new NullPointerException( "s" );
1027                }
1028    
1029                s.getHeadContent().setLength( 0 );
1030                if ( this.specification != null )
1031                {
1032                    s.getHeadContent().append( getLicenseSection( this.specification ) );
1033                }
1034            }
1035    
1036            /**
1037             * Edits the annotations section of the source code of the editor.
1038             *
1039             * @param s The section to edit.
1040             *
1041             * @throws NullPointerException if {@code s} is {@code null}.
1042             * @throws IOException if editing {@code s} fails.
1043             */
1044            public void editAnnotationsSection( final Section s ) throws IOException
1045            {
1046                if ( s == null )
1047                {
1048                    throw new NullPointerException( "s" );
1049                }
1050    
1051                s.getHeadContent().setLength( 0 );
1052                if ( this.specification != null )
1053                {
1054                    s.getHeadContent().append( getAnnotationsSection( this.specification ) );
1055                }
1056            }
1057    
1058            /**
1059             * Edits the documentation section of the source code of the editor.
1060             *
1061             * @param s The section to edit.
1062             *
1063             * @throws NullPointerException if {@code s} is {@code null}.
1064             * @throws IOException if editing {@code s} fails.
1065             */
1066            public void editDocumentationSection( final Section s ) throws IOException
1067            {
1068                if ( s == null )
1069                {
1070                    throw new NullPointerException( "s" );
1071                }
1072    
1073                s.getHeadContent().setLength( 0 );
1074                if ( this.specification != null )
1075                {
1076                    s.getHeadContent().append( getDocumentationSection( this.specification ) );
1077                }
1078            }
1079    
1080        }
1081    
1082        /**
1083         * Extension to {@code JavaEditor} for editing implementation source code.
1084         *
1085         * @author <a href="mailto:cs@jomc.org">Christian Schulte</a>
1086         * @version $Id: JavaSources.java 891 2009-11-02 03:40:00Z schulte2005 $
1087         */
1088        public class JavaImplementationEditor extends JavaEditor
1089        {
1090    
1091            /** The implementation to edit. */
1092            private Implementation implementation;
1093    
1094            /** Flag indicating that the source code of the editor contains a constructors section. */
1095            private boolean constructorsSectionPresent;
1096    
1097            /** Flag indicating that the source code of the editor contains a default constructor section. */
1098            private boolean defaultConstructorSectionPresent;
1099    
1100            /** Flag indicating that the source code of the editor contains a messages section. */
1101            private boolean messagesSectionPresent;
1102    
1103            /** Flag indicating that the source code of the editor contains a dependencies section. */
1104            private boolean dependenciesSectionPresent;
1105    
1106            /** Flag indicating that the source code of the editor contains a properties section. */
1107            private boolean propertiesSectionPresent;
1108    
1109            /**
1110             * Creates a new {@code JavaImplementationEditor} instance for editing the source code of a given implementation.
1111             *
1112             * @param implementation The implementation to edit.
1113             */
1114            public JavaImplementationEditor( final Implementation implementation )
1115            {
1116                super();
1117                this.implementation = implementation;
1118            }
1119    
1120            /**
1121             * Creates a new {@code JavaImplementationEditor} instance for editing the source code of a given implementation
1122             * taking a {@code LineEditor} to chain.
1123             *
1124             * @param lineEditor The editor to chain.
1125             * @param implementation The implementation to edit.
1126             */
1127            public JavaImplementationEditor( final LineEditor lineEditor, final Implementation implementation )
1128            {
1129                super( lineEditor );
1130                this.implementation = implementation;
1131            }
1132    
1133            @Override
1134            public String getOutput( final Section section ) throws IOException
1135            {
1136                if ( section == null )
1137                {
1138                    throw new NullPointerException( "section" );
1139                }
1140    
1141                this.constructorsSectionPresent = false;
1142                this.defaultConstructorSectionPresent = false;
1143                this.messagesSectionPresent = false;
1144                this.dependenciesSectionPresent = false;
1145                this.propertiesSectionPresent = false;
1146                return super.getOutput( section );
1147            }
1148    
1149            @Override
1150            public void editSection( final Section section ) throws IOException
1151            {
1152                if ( section == null )
1153                {
1154                    throw new NullPointerException( "section" );
1155                }
1156    
1157                super.editSection( section );
1158    
1159                if ( section.getName() != null )
1160                {
1161                    if ( CONSTRUCTORS_SECTION_NAME.equals( section.getName() ) )
1162                    {
1163                        this.editConstructorsSection( section );
1164                        this.constructorsSectionPresent = true;
1165                    }
1166                    else if ( DEFAULT_CONSTRUCTOR_SECTION_NAME.equals( section.getName() ) )
1167                    {
1168                        this.editDefaultConstructorSection( section );
1169                        this.defaultConstructorSectionPresent = true;
1170                    }
1171                    else if ( DEPENDENCIES_SECTION_NAME.equals( section.getName() ) )
1172                    {
1173                        this.editDependenciesSection( section );
1174                        this.dependenciesSectionPresent = true;
1175                    }
1176                    else if ( MESSAGES_SECTION_NAME.equals( section.getName() ) )
1177                    {
1178                        this.editMessagesSection( section );
1179                        this.messagesSectionPresent = true;
1180                    }
1181                    else if ( PROPERTIES_SECTION_NAME.equals( section.getName() ) )
1182                    {
1183                        this.editPropertiesSection( section );
1184                        this.propertiesSectionPresent = true;
1185                    }
1186                }
1187            }
1188    
1189            /**
1190             * Edits the license section of the source code of the editor.
1191             *
1192             * @param s The section to edit.
1193             *
1194             * @throws IOException if editing {@code s} fails.
1195             */
1196            public void editLicenseSection( final Section s ) throws IOException
1197            {
1198                if ( s == null )
1199                {
1200                    throw new NullPointerException( "s" );
1201                }
1202    
1203                s.getHeadContent().setLength( 0 );
1204                if ( this.implementation != null )
1205                {
1206                    s.getHeadContent().append( getLicenseSection( this.implementation ) );
1207                }
1208            }
1209    
1210            /**
1211             * Edits the annotations section of the source code of the editor.
1212             *
1213             * @param s The section to edit.
1214             *
1215             * @throws NullPointerException if {@code s} is {@code null}.
1216             * @throws IOException if editing {@code s} fails.
1217             */
1218            public void editAnnotationsSection( final Section s ) throws IOException
1219            {
1220                if ( s == null )
1221                {
1222                    throw new NullPointerException( "s" );
1223                }
1224    
1225                s.getHeadContent().setLength( 0 );
1226                if ( this.implementation != null )
1227                {
1228                    s.getHeadContent().append( getAnnotationsSection( this.implementation ) );
1229                }
1230            }
1231    
1232            /**
1233             * Edits the documentation section of the source code of the editor.
1234             *
1235             * @param s The section to edit.
1236             *
1237             * @throws NullPointerException if {@code s} is {@code null}.
1238             * @throws IOException if editing {@code s} fails.
1239             */
1240            public void editDocumentationSection( final Section s ) throws IOException
1241            {
1242                if ( s == null )
1243                {
1244                    throw new NullPointerException( "s" );
1245                }
1246    
1247                s.getHeadContent().setLength( 0 );
1248                if ( this.implementation != null )
1249                {
1250                    s.getHeadContent().append( getDocumentationSection( this.implementation ) );
1251                }
1252            }
1253    
1254            /**
1255             * Edits the constructors section of the source code of the editor.
1256             *
1257             * @param s The section to edit.
1258             *
1259             * @throws NullPointerException if {@code s} is {@code null}.
1260             * @throws IOException if editing {@code s} fails.
1261             */
1262            public void editConstructorsSection( final Section s ) throws IOException
1263            {
1264                if ( s == null )
1265                {
1266                    throw new NullPointerException( "s" );
1267                }
1268    
1269                s.getHeadContent().setLength( 0 );
1270                s.getTailContent().setLength( 0 );
1271    
1272                if ( this.implementation != null )
1273                {
1274                    s.getHeadContent().append( getConstructorsSectionHeadContent( this.implementation ) );
1275                    s.getTailContent().append( getConstructorsSectionTailContent( this.implementation ) );
1276                }
1277    
1278                for ( Section child : s.getSections() )
1279                {
1280                    if ( child.getName() != null && DEFAULT_CONSTRUCTOR_SECTION_NAME.equals( child.getName() ) )
1281                    {
1282                        this.defaultConstructorSectionPresent = true;
1283                        break;
1284                    }
1285                }
1286    
1287                if ( !this.defaultConstructorSectionPresent )
1288                {
1289                    final Section defaultCtor = new Section();
1290                    defaultCtor.setName( DEFAULT_CONSTRUCTOR_SECTION_NAME );
1291                    defaultCtor.setStartingLine( "        // SECTION-START[" + DEFAULT_CONSTRUCTOR_SECTION_NAME + "]" );
1292                    defaultCtor.setEndingLine( "        // SECTION-END" );
1293                    defaultCtor.getHeadContent().append( "        super();" ).append( this.getLineSeparator() );
1294                    s.getSections().add( defaultCtor );
1295                    this.defaultConstructorSectionPresent = true;
1296                }
1297            }
1298    
1299            /**
1300             * Edits the default constructor section of the source code of the editor.
1301             *
1302             * @param s The section to edit.
1303             *
1304             * @throws NullPointerException if {@code s} is {@code null}.
1305             * @throws IOException if editing {@code s} fails.
1306             */
1307            public void editDefaultConstructorSection( final Section s ) throws IOException
1308            {
1309                if ( s == null )
1310                {
1311                    throw new NullPointerException( "s" );
1312                }
1313    
1314                if ( s.getHeadContent().toString().trim().length() == 0 )
1315                {
1316                    s.getHeadContent().setLength( 0 );
1317    
1318                    if ( this.implementation != null )
1319                    {
1320                        s.getHeadContent().append( "        super();" ).append( this.getLineSeparator() );
1321                    }
1322                }
1323            }
1324    
1325            /**
1326             * Edits the dependencies section of the source code of the editor.
1327             *
1328             * @param s The section to edit.
1329             *
1330             * @throws NullPointerException if {@code s} is {@code null}.
1331             * @throws IOException if editing {@code s} fails.
1332             */
1333            public void editDependenciesSection( final Section s ) throws IOException
1334            {
1335                if ( s == null )
1336                {
1337                    throw new NullPointerException( "s" );
1338                }
1339    
1340                s.getHeadContent().setLength( 0 );
1341                if ( this.implementation != null )
1342                {
1343                    s.getHeadContent().append( getDependenciesSection( this.implementation ) );
1344                }
1345            }
1346    
1347            /**
1348             * Edits the messages section of the source code of the editor.
1349             *
1350             * @param s The section to edit.
1351             *
1352             * @throws NullPointerException if {@code s} is {@code null}.
1353             * @throws IOException if editing {@code s} fails.
1354             */
1355            public void editMessagesSection( final Section s ) throws IOException
1356            {
1357                if ( s == null )
1358                {
1359                    throw new NullPointerException( "s" );
1360                }
1361    
1362                s.getHeadContent().setLength( 0 );
1363                if ( this.implementation != null )
1364                {
1365                    s.getHeadContent().append( getMessagesSection( this.implementation ) );
1366                }
1367            }
1368    
1369            /**
1370             * Edits the properties section of the source code of the editor.
1371             *
1372             * @param s The section to edit.
1373             *
1374             * @throws NullPointerException if {@code s} is {@code null}.
1375             * @throws IOException if editing {@code s} fails.
1376             */
1377            public void editPropertiesSection( final Section s ) throws IOException
1378            {
1379                if ( s == null )
1380                {
1381                    throw new NullPointerException( "s" );
1382                }
1383    
1384                s.getHeadContent().setLength( 0 );
1385                if ( this.implementation != null )
1386                {
1387                    s.getHeadContent().append( getPropertiesSection( this.implementation ) );
1388                }
1389            }
1390    
1391            /**
1392             * Gets a flag indicating that the source code of the editor contains a constructors section.
1393             *
1394             * @return {@code true} if the source code of the editor contains a constructors section; {@code false} if the
1395             * source code of the editor does not contain a constructors section.
1396             */
1397            public boolean isConstructorsSectionPresent()
1398            {
1399                return this.constructorsSectionPresent;
1400            }
1401    
1402            /**
1403             * Gets a flag indicating that the source code of the editor contains a default constructor section.
1404             *
1405             * @return {@code true} if the source code of the editor contains a default constructor section; {@code false}
1406             * if the source code of the editor does not contain a default constructor section.
1407             */
1408            public boolean isDefaultConstructorSectionPresent()
1409            {
1410                return this.defaultConstructorSectionPresent;
1411            }
1412    
1413            /**
1414             * Gets a flag indicating that the source code of the editor contains a messages section.
1415             *
1416             * @return {@code true} if the source code of the editor contains a messages section; {@code false}
1417             * if the source code of the editor does not contain a messages section.
1418             */
1419            public boolean isMessagesSectionPresent()
1420            {
1421                return this.messagesSectionPresent;
1422            }
1423    
1424            /**
1425             * Gets a flag indicating that the source code of the editor contains a dependencies section.
1426             *
1427             * @return {@code true} if the source code of the editor contains a dependencies section; {@code false}
1428             * if the source code of the editor does not contain a dependencies section.
1429             */
1430            public boolean isDependenciesSectionPresent()
1431            {
1432                return this.dependenciesSectionPresent;
1433            }
1434    
1435            /**
1436             * Gets a flag indicating that the source code of the editor contains a properties section.
1437             *
1438             * @return {@code true} if the source code of the editor contains a properties section; {@code false}
1439             * if the source code of the editor does not contain a properties section.
1440             */
1441            public boolean isPropertiesSectionPresent()
1442            {
1443                return this.propertiesSectionPresent;
1444            }
1445    
1446        }
1447    
1448    }