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 744 2009-10-06 04:43:21Z 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 744 2009-10-06 04:43:21Z 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() )
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() )
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                    FileUtils.writeStringToFile( f, edited, this.getOutputEncoding() );
312                    this.log( Level.INFO, this.getMessage( "editing", new Object[]
313                        {
314                            f.getCanonicalPath()
315                        } ), null );
316    
317                }
318            }
319        }
320    
321        /**
322         * Manages the source code of a given implementation of the modules of the instance.
323         *
324         * @param implementation The implementation to process.
325         * @param sourcesDirectory The directory holding the sources to manage.
326         *
327         * @throws NullPointerException if {@code implementation} or {@code sourcesDirectory} is {@code null}.
328         * @throws IOException if managing sources fails.
329         *
330         * @see #getImplementationEditor(org.jomc.model.Implementation)
331         */
332        public void manageSources( final Implementation implementation, final File sourcesDirectory ) throws IOException
333        {
334            if ( implementation == null )
335            {
336                throw new NullPointerException( "implementation" );
337            }
338            if ( sourcesDirectory == null )
339            {
340                throw new NullPointerException( "sourcesDirectory" );
341            }
342    
343            if ( this.isJavaClassDeclaration( implementation ) )
344            {
345                final File f = new File( sourcesDirectory, implementation.getClazz().replace( '.', '/' ) + ".java" );
346                final String content = f.exists()
347                                       ? FileUtils.readFileToString( f, this.getInputEncoding() )
348                                       : this.getImplementationTemplate( implementation );
349    
350                final JavaImplementationEditor editor = this.getImplementationEditor( implementation );
351                final String edited;
352                try
353                {
354                    edited = editor.edit( content );
355                }
356                catch ( final IOException e )
357                {
358                    throw (IOException) new IOException( this.getMessage( "failedEditing", new Object[]
359                        {
360                            f.getCanonicalPath(), e.getMessage()
361                        } ) ).initCause( e );
362    
363                }
364    
365                if ( !editor.isLicenseSectionPresent() )
366                {
367                    this.log( Level.INFO, this.getMessage( "missingOptionalSection", new Object[]
368                        {
369                            LICENSE_SECTION_NAME,
370                            f.getCanonicalPath()
371                        } ), null );
372    
373                }
374    
375                if ( !editor.isAnnotationsSectionPresent() )
376                {
377                    throw new IOException( this.getMessage( "missingSection", new Object[]
378                        {
379                            ANNOTATIONS_SECTION_NAME,
380                            f.getCanonicalPath()
381                        } ) );
382    
383                }
384    
385                if ( !editor.isDocumentationSectionPresent() )
386                {
387                    this.log( Level.INFO, this.getMessage( "missingOptionalSection", new Object[]
388                        {
389                            DOCUMENTATION_SECTION_NAME,
390                            f.getCanonicalPath()
391                        } ), null );
392    
393                }
394    
395                if ( !editor.isConstructorsSectionPresent() )
396                {
397                    final Specifications specifications =
398                        this.getModules().getSpecifications( implementation.getIdentifier() );
399    
400                    if ( specifications != null &&
401                         !( specifications.getSpecification().isEmpty() && specifications.getReference().isEmpty() ) )
402                    {
403                        throw new IOException( this.getMessage( "missingSection", new Object[]
404                            {
405                                CONSTRUCTORS_SECTION_NAME,
406                                f.getCanonicalPath()
407                            } ) );
408    
409                    }
410                    else
411                    {
412                        this.log( Level.INFO, this.getMessage( "missingOptionalSection", new Object[]
413                            {
414                                CONSTRUCTORS_SECTION_NAME,
415                                f.getCanonicalPath()
416                            } ), null );
417    
418                    }
419                }
420                else if ( !editor.isDefaultConstructorSectionPresent() )
421                {
422                    throw new IOException( this.getMessage( "missingSection", new Object[]
423                        {
424                            DEFAULT_CONSTRUCTOR_SECTION_NAME,
425                            f.getCanonicalPath()
426                        } ) );
427    
428                }
429    
430                if ( !editor.isPropertiesSectionPresent() )
431                {
432                    final Properties properties = this.getModules().getProperties( implementation.getIdentifier() );
433    
434                    if ( properties != null && !properties.getProperty().isEmpty() )
435                    {
436                        throw new IOException( this.getMessage( "missingSection", new Object[]
437                            {
438                                PROPERTIES_SECTION_NAME,
439                                f.getCanonicalPath()
440                            } ) );
441    
442                    }
443                    else
444                    {
445                        this.log( Level.INFO, this.getMessage( "missingOptionalSection", new Object[]
446                            {
447                                PROPERTIES_SECTION_NAME,
448                                f.getCanonicalPath()
449                            } ), null );
450    
451                    }
452                }
453    
454                if ( !editor.isDependenciesSectionPresent() )
455                {
456                    final Dependencies dependencies = this.getModules().getDependencies( implementation.getIdentifier() );
457    
458                    if ( dependencies != null && !dependencies.getDependency().isEmpty() )
459                    {
460                        throw new IOException( this.getMessage( "missingSection", new Object[]
461                            {
462                                DEPENDENCIES_SECTION_NAME,
463                                f.getCanonicalPath()
464                            } ) );
465    
466                    }
467                    else
468                    {
469                        this.log( Level.INFO, this.getMessage( "missingOptionalSection", new Object[]
470                            {
471                                DEPENDENCIES_SECTION_NAME,
472                                f.getCanonicalPath()
473                            } ), null );
474    
475                    }
476                }
477    
478                if ( !editor.isMessagesSectionPresent() )
479                {
480                    final Messages messages = this.getModules().getMessages( implementation.getIdentifier() );
481    
482                    if ( messages != null && !messages.getMessage().isEmpty() )
483                    {
484                        throw new IOException( this.getMessage( "missingSection", new Object[]
485                            {
486                                MESSAGES_SECTION_NAME,
487                                f.getCanonicalPath()
488                            } ) );
489    
490                    }
491                    else
492                    {
493                        this.log( Level.INFO, this.getMessage( "missingOptionalSection", new Object[]
494                            {
495                                MESSAGES_SECTION_NAME,
496                                f.getCanonicalPath()
497                            } ), null );
498    
499                    }
500                }
501    
502                if ( !edited.equals( content ) )
503                {
504                    if ( !f.getParentFile().exists() && !f.getParentFile().mkdirs() )
505                    {
506                        throw new IOException( this.getMessage( "failedCreatingDirectory", new Object[]
507                            {
508                                f.getParentFile().getAbsolutePath()
509                            } ) );
510    
511                    }
512    
513                    FileUtils.writeStringToFile( f, edited, this.getOutputEncoding() );
514                    this.log( Level.INFO, this.getMessage( "editing", new Object[]
515                        {
516                            f.getCanonicalPath()
517                        } ), null );
518    
519                }
520            }
521        }
522    
523        /**
524         * Gets a new editor for editing Java specification source code.
525         *
526         * @param specification The specification to create a new editor for.
527         *
528         * @return A new editor for editing the source code of {@code specification}.
529         *
530         * @throws NullPointerException if {@code specification} is {@code null}.
531         */
532        public JavaSpecificationEditor getSpecificationEditor( final Specification specification )
533        {
534            if ( specification == null )
535            {
536                throw new NullPointerException( "specification" );
537            }
538    
539            return new JavaSpecificationEditor( new TrailingWhitespaceEditor(), specification );
540        }
541    
542        /**
543         * Gets a new editor for editing Java implementation source code.
544         *
545         * @param implementation The implementation to create a new editor for.
546         *
547         * @return A new editor for editing the source code of {@code implementation}.
548         *
549         * @throws NullPointerException if {@code implementation} is {@code null}.
550         */
551        public JavaImplementationEditor getImplementationEditor( final Implementation implementation )
552        {
553            if ( implementation == null )
554            {
555                throw new NullPointerException( "implementation" );
556            }
557    
558            return new JavaImplementationEditor( new TrailingWhitespaceEditor(), implementation );
559        }
560    
561        /**
562         * Gets the velocity context used for merging templates.
563         *
564         * @return The velocity context used for merging templates.
565         */
566        @Override
567        public VelocityContext getVelocityContext()
568        {
569            final VelocityContext ctx = super.getVelocityContext();
570            ctx.put( "generatorName", GENERATOR_NAME );
571            ctx.put( "generatorVersion", GENERATOR_VERSION );
572            return ctx;
573        }
574    
575        /**
576         * Gets the Java source code template of specification.
577         *
578         * @param specification The specification to get the source code template of.
579         *
580         * @throws IOException if getting the source code section fails.
581         */
582        private String getSpecificationTemplate( final Specification specification ) throws IOException
583        {
584            final StringWriter writer = new StringWriter();
585            final VelocityContext ctx = this.getVelocityContext();
586            final Template template = this.getVelocityTemplate( SPECIFICATION_TEMPLATE );
587            ctx.put( "specification", specification );
588            ctx.put( "template", template );
589            template.merge( ctx, writer );
590            writer.close();
591            return writer.toString();
592        }
593    
594        /**
595         * Gets the Java source code template of an implementation.
596         *
597         * @param implementation The implementation to get the source code template of.
598         *
599         * @throws IOException if getting the source code section fails.
600         */
601        private String getImplementationTemplate( final Implementation implementation ) throws IOException
602        {
603            final StringWriter writer = new StringWriter();
604            final VelocityContext ctx = this.getVelocityContext();
605            final Template template = this.getVelocityTemplate( IMPLEMENTATION_TEMPLATE );
606            ctx.put( "implementation", implementation );
607            ctx.put( "template", template );
608            template.merge( ctx, writer );
609            writer.close();
610            return writer.toString();
611        }
612    
613        /**
614         * Gets the Java source code of the license section of a specification.
615         *
616         * @param specification The specification to get the source code of the license section of.
617         *
618         * @throws IOException if getting the source code section fails.
619         */
620        private String getLicenseSection( final Specification specification ) throws IOException
621        {
622            final StringWriter writer = new StringWriter();
623            final VelocityContext ctx = this.getVelocityContext();
624            final Template template = this.getVelocityTemplate( SPECIFICATION_LICENSE_TEMPLATE );
625            ctx.put( "specification", specification );
626            ctx.put( "template", template );
627            template.merge( ctx, writer );
628            writer.close();
629            return writer.toString();
630        }
631    
632        /**
633         * Gets the Java source code of the license section of an implementation..
634         *
635         * @param implementation The implementation to get the source code of the license section of.
636         *
637         * @throws IOException if getting the source code section fails.
638         */
639        private String getLicenseSection( final Implementation implementation ) throws IOException
640        {
641            final StringWriter writer = new StringWriter();
642            final VelocityContext ctx = this.getVelocityContext();
643            final Template template = this.getVelocityTemplate( IMPLEMENTATION_LICENSE_TEMPLATE );
644            ctx.put( "implementation", implementation );
645            ctx.put( "template", template );
646            template.merge( ctx, writer );
647            writer.close();
648            return writer.toString();
649        }
650    
651        /**
652         * Gets the Java source code of the specification annotations section.
653         *
654         * @param specification The specification to get the source code of the annotations section of.
655         *
656         * @throws IOException if getting the source code section fails.
657         */
658        private String getAnnotationsSection( final Specification specification ) throws IOException
659        {
660            final StringWriter writer = new StringWriter();
661            final VelocityContext ctx = this.getVelocityContext();
662            final Template template = this.getVelocityTemplate( SPECIFICATION_ANNOTATIONS_TEMPLATE );
663            ctx.put( "specification", specification );
664            ctx.put( "template", template );
665            template.merge( ctx, writer );
666            writer.close();
667            return writer.toString();
668        }
669    
670        /**
671         * Gets the Java source code of the implementation annotations section.
672         *
673         * @param implementation The implementation to get the source code of the annotations section of.
674         *
675         * @throws IOException if getting the source code section fails.
676         */
677        private String getAnnotationsSection( final Implementation implementation ) throws IOException
678        {
679            final StringWriter writer = new StringWriter();
680            final VelocityContext ctx = this.getVelocityContext();
681            final Template template = this.getVelocityTemplate( IMPLEMENTATION_ANNOTATIONS_TEMPLATE );
682            ctx.put( "implementation", implementation );
683            ctx.put( "template", template );
684            template.merge( ctx, writer );
685            writer.close();
686            return writer.toString();
687        }
688    
689        /**
690         * Gets the Java source code of the documentation section of a specification.
691         *
692         * @param specification The specification to get the source code section of.
693         *
694         * @throws IOException if getting the source code section fails.
695         */
696        private String getDocumentationSection( final Specification specification ) throws IOException
697        {
698            final StringWriter writer = new StringWriter();
699            final VelocityContext ctx = this.getVelocityContext();
700            final Template template = this.getVelocityTemplate( SPECIFICATION_DOCUMENTATION_TEMPLATE );
701            ctx.put( "specification", specification );
702            ctx.put( "template", template );
703            template.merge( ctx, writer );
704            writer.close();
705            return writer.toString();
706        }
707    
708        /**
709         * Gets the Java source code of the documentation section of an implementation.
710         *
711         * @param implementation The implementation to get the source code section of.
712         *
713         * @throws IOException if getting the source code section fails.
714         */
715        private String getDocumentationSection( final Implementation implementation ) throws IOException
716        {
717            final StringWriter writer = new StringWriter();
718            final VelocityContext ctx = this.getVelocityContext();
719            final Template template = this.getVelocityTemplate( IMPLEMENTATION_DOCUMENTATION_TEMPLATE );
720            ctx.put( "implementation", implementation );
721            ctx.put( "template", template );
722            template.merge( ctx, writer );
723            writer.close();
724            return writer.toString();
725        }
726    
727        /**
728         * Gets the Java source code of the constructors section head content of an implementation.
729         *
730         * @param implementation The implementation to get the constructors section head content of.
731         *
732         * @throws IOException if getting the source code section fails.
733         */
734        private String getConstructorsSectionHeadContent( final Implementation implementation ) throws IOException
735        {
736            final StringWriter writer = new StringWriter();
737            final VelocityContext ctx = this.getVelocityContext();
738            final Template template = this.getVelocityTemplate( CONSTRUCTORS_HEAD_TEMPLATE );
739            ctx.put( "implementation", implementation );
740            ctx.put( "template", template );
741            template.merge( ctx, writer );
742            writer.close();
743            return writer.toString();
744        }
745    
746        /**
747         * Gets the Java source code of the constructors section tail content of an implementation.
748         *
749         * @param implementation The implementation to get the constructors section tail content of.
750         *
751         * @throws IOException if getting the source code section fails.
752         */
753        private String getConstructorsSectionTailContent( final Implementation implementation ) throws IOException
754        {
755            final StringWriter writer = new StringWriter();
756            final VelocityContext ctx = this.getVelocityContext();
757            final Template template = this.getVelocityTemplate( CONSTRUCTORS_TAIL_TEMPLATE );
758            ctx.put( "implementation", implementation );
759            ctx.put( "template", template );
760            template.merge( ctx, writer );
761            writer.close();
762            return writer.toString();
763        }
764    
765        /**
766         * Gets the Java source code of the dependencies section of an implementation.
767         *
768         * @param implementation The implementation to get the source code of the dependencies section of.
769         *
770         * @throws IOException if getting the source code section fails.
771         */
772        private String getDependenciesSection( final Implementation implementation ) throws IOException
773        {
774            final StringWriter writer = new StringWriter();
775            final VelocityContext ctx = this.getVelocityContext();
776            final Template template = this.getVelocityTemplate( DEPENDENCIES_TEMPLATE );
777            ctx.put( "implementation", implementation );
778            ctx.put( "template", template );
779            template.merge( ctx, writer );
780            writer.close();
781            return writer.toString();
782        }
783    
784        /**
785         * Gets the Java source code of the properties section of an implementation.
786         *
787         * @param implementation The implementation to get the source code of the properties section of.
788         *
789         * @throws IOException if getting the source code section fails.
790         */
791        private String getPropertiesSection( final Implementation implementation ) throws IOException
792        {
793            final StringWriter writer = new StringWriter();
794            final VelocityContext ctx = this.getVelocityContext();
795            final Template template = this.getVelocityTemplate( PROPERTIES_TEMPLATE );
796            ctx.put( "implementation", implementation );
797            ctx.put( "template", template );
798            template.merge( ctx, writer );
799            writer.close();
800            return writer.toString();
801        }
802    
803        /**
804         * Gets the Java source code of the messages section of an implementation.
805         *
806         * @param implementation The implementation to get the source code of the messages section of.
807         *
808         * @throws IOException if getting the source code section fails.
809         */
810        private String getMessagesSection( final Implementation implementation ) throws IOException
811        {
812            final StringWriter writer = new StringWriter();
813            final VelocityContext ctx = this.getVelocityContext();
814            final Template template = this.getVelocityTemplate( MESSAGES_TEMPLATE );
815            ctx.put( "implementation", implementation );
816            ctx.put( "template", template );
817            template.merge( ctx, writer );
818            writer.close();
819            return writer.toString();
820        }
821    
822        private String getMessage( final String key, final Object args )
823        {
824            final ResourceBundle b = ResourceBundle.getBundle( JavaSources.class.getName().replace( '.', '/' ) );
825            final MessageFormat f = new MessageFormat( b.getString( key ) );
826            return f.format( args );
827        }
828    
829        /**
830         * Extension to {@code SectionEditor} for editing Java source code.
831         *
832         * @author <a href="mailto:cs@jomc.org">Christian Schulte</a>
833         * @version $Id: JavaSources.java 744 2009-10-06 04:43:21Z schulte2005 $
834         */
835        public abstract class JavaEditor extends SectionEditor
836        {
837    
838            /** Flag indicating that the source code of the editor contains a license section. */
839            private boolean licenseSectionPresent;
840    
841            /** Flag indicating that the source code of the editor contains an annotations section. */
842            private boolean annotationsSectionPresent;
843    
844            /** Flag indicating that the source code of the editor contains a documentation section. */
845            private boolean documentationSectionPresent;
846    
847            /** Creates a new {@code JavaEditor} instance. */
848            public JavaEditor()
849            {
850                super();
851            }
852    
853            /**
854             * Creates a new {@code JavaEditor} instance taking a {@code LineEditor} to chain.
855             *
856             * @param lineEditor The editor to chain.
857             */
858            public JavaEditor( final LineEditor lineEditor )
859            {
860                super( lineEditor );
861            }
862    
863            @Override
864            public String getOutput( final Section section ) throws IOException
865            {
866                if ( section == null )
867                {
868                    throw new NullPointerException( "section" );
869                }
870    
871                this.licenseSectionPresent = false;
872                this.annotationsSectionPresent = false;
873                this.documentationSectionPresent = false;
874                return super.getOutput( section );
875            }
876    
877            @Override
878            public void editSection( final Section section ) throws IOException
879            {
880                if ( section == null )
881                {
882                    throw new NullPointerException( "section" );
883                }
884    
885                if ( section.getName() != null )
886                {
887                    if ( LICENSE_SECTION_NAME.equals( section.getName() ) )
888                    {
889                        this.editLicenseSection( section );
890                        this.licenseSectionPresent = true;
891                    }
892                    if ( ANNOTATIONS_SECTION_NAME.equals( section.getName() ) )
893                    {
894                        this.editAnnotationsSection( section );
895                        this.annotationsSectionPresent = true;
896                    }
897                    if ( DOCUMENTATION_SECTION_NAME.equals( section.getName() ) )
898                    {
899                        this.editDocumentationSection( section );
900                        this.documentationSectionPresent = true;
901                    }
902                }
903            }
904    
905            /**
906             * Edits the license section of the source code of the editor.
907             *
908             * @param s The section to edit.
909             *
910             * @throws NullPointerException if {@code s} is {@code null}.
911             * @throws IOException if editing {@code s} fails.
912             */
913            public abstract void editLicenseSection( final Section s ) throws IOException;
914    
915            /**
916             * Edits the annotations section of the source code of the editor.
917             *
918             * @param s The section to edit.
919             *
920             * @throws NullPointerException if {@code s} is {@code null}.
921             * @throws IOException if editing {@code s} fails.
922             */
923            public abstract void editAnnotationsSection( final Section s ) throws IOException;
924    
925            /**
926             * Edits the documentation section of the source code of the editor.
927             *
928             * @param s The section to edit.
929             *
930             * @throws NullPointerException if {@code s} is {@code null}.
931             * @throws IOException if editing {@code s} fails.
932             */
933            public abstract void editDocumentationSection( final Section s ) throws IOException;
934    
935            /**
936             * Gets a flag indicating that the source code of the editor contains a license section.
937             *
938             * @return {@code true} if the source code of the editor contains a license section; {@code false} if the
939             * source code of the editor does not contain a license section.
940             */
941            public boolean isLicenseSectionPresent()
942            {
943                return this.licenseSectionPresent;
944            }
945    
946            /**
947             * Gets a flag indicating that the source code of the editor contains an annotations section.
948             *
949             * @return {@code true} if the source code of the editor contains an annotations section; {@code false} if the
950             * source code of the editor does not contain an annotations section.
951             */
952            public boolean isAnnotationsSectionPresent()
953            {
954                return this.annotationsSectionPresent;
955            }
956    
957            /**
958             * Gets a flag indicating that the source code of the editor contains a documentation section.
959             *
960             * @return {@code true} if the source code of the editor contains a documentation section; {@code false} if the
961             * source code of the editor does not contain a documentation section.
962             */
963            public boolean isDocumentationSectionPresent()
964            {
965                return this.documentationSectionPresent;
966            }
967    
968        }
969    
970        /**
971         * Extension to {@code JavaEditor} for editing specification source code.
972         *
973         * @author <a href="mailto:cs@jomc.org">Christian Schulte</a>
974         * @version $Id: JavaSources.java 744 2009-10-06 04:43:21Z schulte2005 $
975         */
976        public class JavaSpecificationEditor extends JavaEditor
977        {
978    
979            /** The specification to edit. */
980            private Specification specification;
981    
982            /**
983             * Creates a new {@code JavaSpecificationEditor} instance for editing the source code of a given specification.
984             *
985             * @param specification The specification to edit.
986             */
987            public JavaSpecificationEditor( final Specification specification )
988            {
989                super();
990                this.specification = specification;
991            }
992    
993            /**
994             * Creates a new {@code JavaSpecificationEditor} instance for editing the source code of a given specification
995             * taking a {@code LineEditor} to chain.
996             *
997             * @param lineEditor The editor to chain.
998             * @param specification The specification to edit.
999             */
1000            public JavaSpecificationEditor( final LineEditor lineEditor, final Specification specification )
1001            {
1002                super( lineEditor );
1003                this.specification = specification;
1004            }
1005    
1006            /**
1007             * Edits the license section of the source code of the editor.
1008             *
1009             * @param s The section to edit.
1010             *
1011             * @throws NullPointerException if {@code s} is {@code null}.
1012             * @throws IOException if editing {@code s} fails.
1013             */
1014            public void editLicenseSection( final Section s ) throws IOException
1015            {
1016                if ( s == null )
1017                {
1018                    throw new NullPointerException( "s" );
1019                }
1020    
1021                s.getHeadContent().setLength( 0 );
1022                if ( this.specification != null )
1023                {
1024                    s.getHeadContent().append( getLicenseSection( this.specification ) );
1025                }
1026            }
1027    
1028            /**
1029             * Edits the annotations section of the source code of the editor.
1030             *
1031             * @param s The section to edit.
1032             *
1033             * @throws NullPointerException if {@code s} is {@code null}.
1034             * @throws IOException if editing {@code s} fails.
1035             */
1036            public void editAnnotationsSection( final Section s ) throws IOException
1037            {
1038                if ( s == null )
1039                {
1040                    throw new NullPointerException( "s" );
1041                }
1042    
1043                s.getHeadContent().setLength( 0 );
1044                if ( this.specification != null )
1045                {
1046                    s.getHeadContent().append( getAnnotationsSection( this.specification ) );
1047                }
1048            }
1049    
1050            /**
1051             * Edits the documentation section of the source code of the editor.
1052             *
1053             * @param s The section to edit.
1054             *
1055             * @throws NullPointerException if {@code s} is {@code null}.
1056             * @throws IOException if editing {@code s} fails.
1057             */
1058            public void editDocumentationSection( final Section s ) throws IOException
1059            {
1060                if ( s == null )
1061                {
1062                    throw new NullPointerException( "s" );
1063                }
1064    
1065                s.getHeadContent().setLength( 0 );
1066                if ( this.specification != null )
1067                {
1068                    s.getHeadContent().append( getDocumentationSection( this.specification ) );
1069                }
1070            }
1071    
1072        }
1073    
1074        /**
1075         * Extension to {@code JavaEditor} for editing implementation source code.
1076         *
1077         * @author <a href="mailto:cs@jomc.org">Christian Schulte</a>
1078         * @version $Id: JavaSources.java 744 2009-10-06 04:43:21Z schulte2005 $
1079         */
1080        public class JavaImplementationEditor extends JavaEditor
1081        {
1082    
1083            /** The implementation to edit. */
1084            private Implementation implementation;
1085    
1086            /** Flag indicating that the source code of the editor contains a constructors section. */
1087            private boolean constructorsSectionPresent;
1088    
1089            /** Flag indicating that the source code of the editor contains a default constructor section. */
1090            private boolean defaultConstructorSectionPresent;
1091    
1092            /** Flag indicating that the source code of the editor contains a messages section. */
1093            private boolean messagesSectionPresent;
1094    
1095            /** Flag indicating that the source code of the editor contains a dependencies section. */
1096            private boolean dependenciesSectionPresent;
1097    
1098            /** Flag indicating that the source code of the editor contains a properties section. */
1099            private boolean propertiesSectionPresent;
1100    
1101            /**
1102             * Creates a new {@code JavaImplementationEditor} instance for editing the source code of a given implementation.
1103             *
1104             * @param implementation The implementation to edit.
1105             */
1106            public JavaImplementationEditor( final Implementation implementation )
1107            {
1108                super();
1109                this.implementation = implementation;
1110            }
1111    
1112            /**
1113             * Creates a new {@code JavaImplementationEditor} instance for editing the source code of a given implementation
1114             * taking a {@code LineEditor} to chain.
1115             *
1116             * @param lineEditor The editor to chain.
1117             * @param implementation The implementation to edit.
1118             */
1119            public JavaImplementationEditor( final LineEditor lineEditor, final Implementation implementation )
1120            {
1121                super( lineEditor );
1122                this.implementation = implementation;
1123            }
1124    
1125            @Override
1126            public String getOutput( final Section section ) throws IOException
1127            {
1128                if ( section == null )
1129                {
1130                    throw new NullPointerException( "section" );
1131                }
1132    
1133                this.constructorsSectionPresent = false;
1134                this.defaultConstructorSectionPresent = false;
1135                this.messagesSectionPresent = false;
1136                this.dependenciesSectionPresent = false;
1137                this.propertiesSectionPresent = false;
1138                return super.getOutput( section );
1139            }
1140    
1141            @Override
1142            public void editSection( final Section section ) throws IOException
1143            {
1144                if ( section == null )
1145                {
1146                    throw new NullPointerException( "section" );
1147                }
1148    
1149                super.editSection( section );
1150    
1151                if ( section.getName() != null )
1152                {
1153                    if ( CONSTRUCTORS_SECTION_NAME.equals( section.getName() ) )
1154                    {
1155                        this.editConstructorsSection( section );
1156                        this.constructorsSectionPresent = true;
1157                    }
1158                    else if ( DEFAULT_CONSTRUCTOR_SECTION_NAME.equals( section.getName() ) )
1159                    {
1160                        this.editDefaultConstructorSection( section );
1161                        this.defaultConstructorSectionPresent = true;
1162                    }
1163                    else if ( DEPENDENCIES_SECTION_NAME.equals( section.getName() ) )
1164                    {
1165                        this.editDependenciesSection( section );
1166                        this.dependenciesSectionPresent = true;
1167                    }
1168                    else if ( MESSAGES_SECTION_NAME.equals( section.getName() ) )
1169                    {
1170                        this.editMessagesSection( section );
1171                        this.messagesSectionPresent = true;
1172                    }
1173                    else if ( PROPERTIES_SECTION_NAME.equals( section.getName() ) )
1174                    {
1175                        this.editPropertiesSection( section );
1176                        this.propertiesSectionPresent = true;
1177                    }
1178                }
1179            }
1180    
1181            /**
1182             * Edits the license section of the source code of the editor.
1183             *
1184             * @param s The section to edit.
1185             *
1186             * @throws IOException if editing {@code s} fails.
1187             */
1188            public void editLicenseSection( final Section s ) throws IOException
1189            {
1190                if ( s == null )
1191                {
1192                    throw new NullPointerException( "s" );
1193                }
1194    
1195                s.getHeadContent().setLength( 0 );
1196                if ( this.implementation != null )
1197                {
1198                    s.getHeadContent().append( getLicenseSection( this.implementation ) );
1199                }
1200            }
1201    
1202            /**
1203             * Edits the annotations section of the source code of the editor.
1204             *
1205             * @param s The section to edit.
1206             *
1207             * @throws NullPointerException if {@code s} is {@code null}.
1208             * @throws IOException if editing {@code s} fails.
1209             */
1210            public void editAnnotationsSection( final Section s ) throws IOException
1211            {
1212                if ( s == null )
1213                {
1214                    throw new NullPointerException( "s" );
1215                }
1216    
1217                s.getHeadContent().setLength( 0 );
1218                if ( this.implementation != null )
1219                {
1220                    s.getHeadContent().append( getAnnotationsSection( this.implementation ) );
1221                }
1222            }
1223    
1224            /**
1225             * Edits the documentation section of the source code of the editor.
1226             *
1227             * @param s The section to edit.
1228             *
1229             * @throws NullPointerException if {@code s} is {@code null}.
1230             * @throws IOException if editing {@code s} fails.
1231             */
1232            public void editDocumentationSection( final Section s ) throws IOException
1233            {
1234                if ( s == null )
1235                {
1236                    throw new NullPointerException( "s" );
1237                }
1238    
1239                s.getHeadContent().setLength( 0 );
1240                if ( this.implementation != null )
1241                {
1242                    s.getHeadContent().append( getDocumentationSection( this.implementation ) );
1243                }
1244            }
1245    
1246            /**
1247             * Edits the constructors section of the source code of the editor.
1248             *
1249             * @param s The section to edit.
1250             *
1251             * @throws NullPointerException if {@code s} is {@code null}.
1252             * @throws IOException if editing {@code s} fails.
1253             */
1254            public void editConstructorsSection( final Section s ) throws IOException
1255            {
1256                if ( s == null )
1257                {
1258                    throw new NullPointerException( "s" );
1259                }
1260    
1261                s.getHeadContent().setLength( 0 );
1262                s.getTailContent().setLength( 0 );
1263    
1264                if ( this.implementation != null )
1265                {
1266                    s.getHeadContent().append( getConstructorsSectionHeadContent( this.implementation ) );
1267                    s.getTailContent().append( getConstructorsSectionTailContent( this.implementation ) );
1268                }
1269    
1270                for ( Section child : s.getSections() )
1271                {
1272                    if ( child.getName() != null && DEFAULT_CONSTRUCTOR_SECTION_NAME.equals( child.getName() ) )
1273                    {
1274                        this.defaultConstructorSectionPresent = true;
1275                        break;
1276                    }
1277                }
1278    
1279                if ( !this.defaultConstructorSectionPresent )
1280                {
1281                    final Section defaultCtor = new Section();
1282                    defaultCtor.setName( DEFAULT_CONSTRUCTOR_SECTION_NAME );
1283                    defaultCtor.setStartingLine( "        // SECTION-START[" + DEFAULT_CONSTRUCTOR_SECTION_NAME + "]" );
1284                    defaultCtor.setEndingLine( "        // SECTION-END" );
1285                    defaultCtor.getHeadContent().append( "        super();" ).append( this.getLineSeparator() );
1286                    s.getSections().add( defaultCtor );
1287                    this.defaultConstructorSectionPresent = true;
1288                }
1289            }
1290    
1291            /**
1292             * Edits the default constructor section of the source code of the editor.
1293             *
1294             * @param s The section to edit.
1295             *
1296             * @throws NullPointerException if {@code s} is {@code null}.
1297             * @throws IOException if editing {@code s} fails.
1298             */
1299            public void editDefaultConstructorSection( final Section s ) throws IOException
1300            {
1301                if ( s == null )
1302                {
1303                    throw new NullPointerException( "s" );
1304                }
1305    
1306                if ( s.getHeadContent().toString().trim().length() == 0 )
1307                {
1308                    s.getHeadContent().setLength( 0 );
1309    
1310                    if ( this.implementation != null )
1311                    {
1312                        s.getHeadContent().append( "        super();" ).append( this.getLineSeparator() );
1313                    }
1314                }
1315            }
1316    
1317            /**
1318             * Edits the dependencies section of the source code of the editor.
1319             *
1320             * @param s The section to edit.
1321             *
1322             * @throws NullPointerException if {@code s} is {@code null}.
1323             * @throws IOException if editing {@code s} fails.
1324             */
1325            public void editDependenciesSection( final Section s ) throws IOException
1326            {
1327                if ( s == null )
1328                {
1329                    throw new NullPointerException( "s" );
1330                }
1331    
1332                s.getHeadContent().setLength( 0 );
1333                if ( this.implementation != null )
1334                {
1335                    s.getHeadContent().append( getDependenciesSection( this.implementation ) );
1336                }
1337            }
1338    
1339            /**
1340             * Edits the messages section of the source code of the editor.
1341             *
1342             * @param s The section to edit.
1343             *
1344             * @throws NullPointerException if {@code s} is {@code null}.
1345             * @throws IOException if editing {@code s} fails.
1346             */
1347            public void editMessagesSection( final Section s ) throws IOException
1348            {
1349                if ( s == null )
1350                {
1351                    throw new NullPointerException( "s" );
1352                }
1353    
1354                s.getHeadContent().setLength( 0 );
1355                if ( this.implementation != null )
1356                {
1357                    s.getHeadContent().append( getMessagesSection( this.implementation ) );
1358                }
1359            }
1360    
1361            /**
1362             * Edits the properties section of the source code of the editor.
1363             *
1364             * @param s The section to edit.
1365             *
1366             * @throws NullPointerException if {@code s} is {@code null}.
1367             * @throws IOException if editing {@code s} fails.
1368             */
1369            public void editPropertiesSection( final Section s ) throws IOException
1370            {
1371                if ( s == null )
1372                {
1373                    throw new NullPointerException( "s" );
1374                }
1375    
1376                s.getHeadContent().setLength( 0 );
1377                if ( this.implementation != null )
1378                {
1379                    s.getHeadContent().append( getPropertiesSection( this.implementation ) );
1380                }
1381            }
1382    
1383            /**
1384             * Gets a flag indicating that the source code of the editor contains a constructors section.
1385             *
1386             * @return {@code true} if the source code of the editor contains a constructors section; {@code false} if the
1387             * source code of the editor does not contain a constructors section.
1388             */
1389            public boolean isConstructorsSectionPresent()
1390            {
1391                return this.constructorsSectionPresent;
1392            }
1393    
1394            /**
1395             * Gets a flag indicating that the source code of the editor contains a default constructor section.
1396             *
1397             * @return {@code true} if the source code of the editor contains a default constructor section; {@code false}
1398             * if the source code of the editor does not contain a default constructor section.
1399             */
1400            public boolean isDefaultConstructorSectionPresent()
1401            {
1402                return this.defaultConstructorSectionPresent;
1403            }
1404    
1405            /**
1406             * Gets a flag indicating that the source code of the editor contains a messages section.
1407             *
1408             * @return {@code true} if the source code of the editor contains a messages section; {@code false}
1409             * if the source code of the editor does not contain a messages section.
1410             */
1411            public boolean isMessagesSectionPresent()
1412            {
1413                return this.messagesSectionPresent;
1414            }
1415    
1416            /**
1417             * Gets a flag indicating that the source code of the editor contains a dependencies section.
1418             *
1419             * @return {@code true} if the source code of the editor contains a dependencies section; {@code false}
1420             * if the source code of the editor does not contain a dependencies section.
1421             */
1422            public boolean isDependenciesSectionPresent()
1423            {
1424                return this.dependenciesSectionPresent;
1425            }
1426    
1427            /**
1428             * Gets a flag indicating that the source code of the editor contains a properties section.
1429             *
1430             * @return {@code true} if the source code of the editor contains a properties section; {@code false}
1431             * if the source code of the editor does not contain a properties section.
1432             */
1433            public boolean isPropertiesSectionPresent()
1434            {
1435                return this.propertiesSectionPresent;
1436            }
1437    
1438        }
1439    
1440    }