001 /*
002 * Copyright (c) 2009 The JOMC Project
003 * Copyright (c) 2005 Christian Schulte <schulte2005@users.sourceforge.net>
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: SourceFileProcessor.java 2164 2010-06-28 03:25:51Z 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.LinkedList;
040 import java.util.List;
041 import java.util.ResourceBundle;
042 import java.util.logging.Level;
043 import org.apache.commons.io.FileUtils;
044 import org.apache.velocity.Template;
045 import org.apache.velocity.VelocityContext;
046 import org.jomc.model.Dependencies;
047 import org.jomc.model.Implementation;
048 import org.jomc.model.Messages;
049 import org.jomc.model.Module;
050 import org.jomc.model.Properties;
051 import org.jomc.model.Specification;
052 import org.jomc.model.Specifications;
053 import org.jomc.tools.model.SourceFileType;
054 import org.jomc.tools.model.SourceFilesType;
055 import org.jomc.tools.model.SourceSectionType;
056 import org.jomc.tools.model.SourceSectionsType;
057 import org.jomc.util.LineEditor;
058 import org.jomc.util.Section;
059 import org.jomc.util.SectionEditor;
060 import org.jomc.util.TrailingWhitespaceEditor;
061
062 /**
063 * Processes source code files.
064 *
065 * <p><b>Use cases</b><br/><ul>
066 * <li>{@link #manageSourceFiles(File) }</li>
067 * <li>{@link #manageSourceFiles(Module, File) }</li>
068 * <li>{@link #manageSourceFiles(Specification, File) }</li>
069 * <li>{@link #manageSourceFiles(Implementation, File) }</li>
070 * </ul></p>
071 *
072 * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
073 * @version $Id: SourceFileProcessor.java 2164 2010-06-28 03:25:51Z schulte2005 $
074 */
075 public class SourceFileProcessor extends JomcTool
076 {
077
078 /** Constant for the name of the constructors source code section. */
079 private static final String CONSTRUCTORS_SECTION_NAME = "Constructors";
080
081 /** Constant for the name of the default constructor source code section. */
082 private static final String DEFAULT_CONSTRUCTOR_SECTION_NAME = "Default Constructor";
083
084 /** Constant for the name of the dependencies source code section. */
085 private static final String DEPENDENCIES_SECTION_NAME = "Dependencies";
086
087 /** Constant for the name of the properties source code section. */
088 private static final String PROPERTIES_SECTION_NAME = "Properties";
089
090 /** Constant for the name of the messages source code section. */
091 private static final String MESSAGES_SECTION_NAME = "Messages";
092
093 /** Constant for the name of the license source code section. */
094 private static final String LICENSE_SECTION_NAME = "License Header";
095
096 /** Constant for the name of the documentation source code section. */
097 private static final String DOCUMENTATION_SECTION_NAME = "Documentation";
098
099 /** Constant for the name of the implementation annotations source code section. */
100 private static final String ANNOTATIONS_SECTION_NAME = "Annotations";
101
102 /** Name of the {@code implementation-constructors-head.vm} template. */
103 private static final String CONSTRUCTORS_HEAD_TEMPLATE = "implementation-constructors-head.vm";
104
105 /** Name of the {@code implementation-constructors-tail.vm} template. */
106 private static final String CONSTRUCTORS_TAIL_TEMPLATE = "implementation-constructors-tail.vm";
107
108 /** Name of the {@code implementation-default-constructor.vm} template. */
109 private static final String DEFAULT_CONSTRUCTOR_TEMPLATE = "implementation-default-constructor.vm";
110
111 /** Name of the {@code implementation-dependencies.vm} template. */
112 private static final String DEPENDENCIES_TEMPLATE = "implementation-dependencies.vm";
113
114 /** Name of the {@code implementation-properties.vm} template. */
115 private static final String PROPERTIES_TEMPLATE = "implementation-properties.vm";
116
117 /** Name of the {@code implementation-messages.vm} template. */
118 private static final String MESSAGES_TEMPLATE = "implementation-messages.vm";
119
120 /** Name of the {@code specification-license.vm} template. */
121 private static final String SPECIFICATION_LICENSE_TEMPLATE = "specification-license.vm";
122
123 /** Name of the {@code implementation-license.vm} template. */
124 private static final String IMPLEMENTATION_LICENSE_TEMPLATE = "implementation-license.vm";
125
126 /** Name of the {@code specification-documentation.vm} template. */
127 private static final String SPECIFICATION_DOCUMENTATION_TEMPLATE = "specification-documentation.vm";
128
129 /** Name of the {@code implementation-documentation.vm} template. */
130 private static final String IMPLEMENTATION_DOCUMENTATION_TEMPLATE = "implementation-documentation.vm";
131
132 /** Name of the {@code Implementation.java.vm} template. */
133 private static final String IMPLEMENTATION_TEMPLATE = "Implementation.java.vm";
134
135 /** Name of the {@code Specification.java.vm} template. */
136 private static final String SPECIFICATION_TEMPLATE = "Specification.java.vm";
137
138 /** Name of the {@code specification-annotations.vm} template. */
139 private static final String SPECIFICATION_ANNOTATIONS_TEMPLATE = "specification-annotations.vm";
140
141 /** Name of the {@code implementation-annotations.vm} template. */
142 private static final String IMPLEMENTATION_ANNOTATIONS_TEMPLATE = "implementation-annotations.vm";
143
144 /** Source files model. */
145 private SourceFilesType sourceFilesType;
146
147 /** Creates a new {@code SourceFileProcessor} instance. */
148 public SourceFileProcessor()
149 {
150 super();
151 }
152
153 /**
154 * Creates a new {@code SourceFileProcessor} instance taking a {@code SourceFileProcessor} instance to initialize
155 * the instance with.
156 *
157 * @param tool The instance to initialize the new instance with,
158 *
159 * @throws NullPointerException if {@code tool} is {@code null}.
160 * @throws IOException if copying {@code tool} fails.
161 */
162 public SourceFileProcessor( final SourceFileProcessor tool ) throws IOException
163 {
164 super( tool );
165 this.sourceFilesType = tool.sourceFilesType != null ? new SourceFilesType( tool.sourceFilesType ) : null;
166 }
167
168 /**
169 * Gets the source files model of the instance.
170 * <p>This accessor method returns a reference to the live object, not a snapshot. Therefore any modification you
171 * make to the returned object will be present inside the object. This is why there is no {@code set} method.</p>
172 *
173 * @return The source files model of the instance.
174 *
175 * @see #getSourceFileType(org.jomc.model.Specification)
176 * @see #getSourceFileType(org.jomc.model.Implementation)
177 */
178 public SourceFilesType getSourceFilesType()
179 {
180 if ( this.sourceFilesType == null )
181 {
182 this.sourceFilesType = new SourceFilesType();
183 }
184
185 return this.sourceFilesType;
186 }
187
188 /**
189 * Gets the model of a specification source file of the modules of the instance.
190 *
191 * @param specification The specification to get a source file model for.
192 *
193 * @return The source file model for {@code specification}.
194 *
195 * @throws NullPointerException if {@code specification} is {@code null}.
196 *
197 * @see #getSourceFilesType()
198 */
199 public SourceFileType getSourceFileType( final Specification specification )
200 {
201 if ( specification == null )
202 {
203 throw new NullPointerException( "specification" );
204 }
205
206 assert this.getModules().getSpecification( specification.getIdentifier() ) != null :
207 "Specification '" + specification.getIdentifier() + "' not found.";
208
209 SourceFileType sourceFileType = this.getSourceFilesType().getSourceFile( specification.getIdentifier() );
210
211 if ( sourceFileType == null )
212 {
213 sourceFileType = specification.getAnyObject( SourceFileType.class );
214 }
215
216 if ( sourceFileType == null )
217 {
218 sourceFileType = new SourceFileType();
219 sourceFileType.setIdentifier( specification.getIdentifier() );
220 sourceFileType.setLocation( specification.getClazz().replace( '.', '/' ) + ".java" );
221 sourceFileType.setTemplate( SPECIFICATION_TEMPLATE );
222 sourceFileType.setSourceSections( new SourceSectionsType() );
223
224 SourceSectionType s = new SourceSectionType();
225 s.setName( LICENSE_SECTION_NAME );
226 s.setHeadTemplate( SPECIFICATION_LICENSE_TEMPLATE );
227 s.setOptional( true );
228 sourceFileType.getSourceSections().getSourceSection().add( s );
229
230 s = new SourceSectionType();
231 s.setName( ANNOTATIONS_SECTION_NAME );
232 s.setHeadTemplate( SPECIFICATION_ANNOTATIONS_TEMPLATE );
233 s.setOptional( false );
234 sourceFileType.getSourceSections().getSourceSection().add( s );
235
236 s = new SourceSectionType();
237 s.setName( DOCUMENTATION_SECTION_NAME );
238 s.setHeadTemplate( SPECIFICATION_DOCUMENTATION_TEMPLATE );
239 s.setOptional( true );
240 sourceFileType.getSourceSections().getSourceSection().add( s );
241
242 final String javaTypeName = this.getJavaTypeName( specification, false );
243 if ( javaTypeName != null )
244 {
245 s = new SourceSectionType();
246 s.setName( javaTypeName );
247 s.setIndentationLevel( 1 );
248 s.setOptional( false );
249 s.setEditable( true );
250 sourceFileType.getSourceSections().getSourceSection().add( s );
251 }
252 }
253
254 return sourceFileType;
255 }
256
257 /**
258 * Gets the model of an implementation source file of the modules of the instance.
259 *
260 * @param implementation The implementation to get a source file model for.
261 *
262 * @return The source file model for {@code implementation}.
263 *
264 * @throws NullPointerException if {@code implementation} is {@code null}.
265 *
266 * @see #getSourceFilesType()
267 */
268 public SourceFileType getSourceFileType( final Implementation implementation )
269 {
270 if ( implementation == null )
271 {
272 throw new NullPointerException( "implementation" );
273 }
274
275 assert this.getModules().getImplementation( implementation.getIdentifier() ) != null :
276 "Implementation '" + implementation.getIdentifier() + "' not found.";
277
278 SourceFileType sourceFileType = this.getSourceFilesType().getSourceFile( implementation.getIdentifier() );
279
280 if ( sourceFileType == null )
281 {
282 sourceFileType = implementation.getAnyObject( SourceFileType.class );
283 }
284
285 if ( sourceFileType == null )
286 {
287 final Specifications specifications = this.getModules().getSpecifications( implementation.getIdentifier() );
288 final Dependencies dependencies = this.getModules().getDependencies( implementation.getIdentifier() );
289 final Messages messages = this.getModules().getMessages( implementation.getIdentifier() );
290 final Properties properties = this.getModules().getProperties( implementation.getIdentifier() );
291
292 sourceFileType = new SourceFileType();
293 sourceFileType.setIdentifier( implementation.getIdentifier() );
294 sourceFileType.setLocation( implementation.getClazz().replace( '.', '/' ) + ".java" );
295 sourceFileType.setTemplate( IMPLEMENTATION_TEMPLATE );
296 sourceFileType.setSourceSections( new SourceSectionsType() );
297
298 SourceSectionType s = new SourceSectionType();
299 s.setName( LICENSE_SECTION_NAME );
300 s.setHeadTemplate( IMPLEMENTATION_LICENSE_TEMPLATE );
301 s.setOptional( true );
302 sourceFileType.getSourceSections().getSourceSection().add( s );
303
304 s = new SourceSectionType();
305 s.setName( ANNOTATIONS_SECTION_NAME );
306 s.setHeadTemplate( IMPLEMENTATION_ANNOTATIONS_TEMPLATE );
307 s.setOptional( false );
308 sourceFileType.getSourceSections().getSourceSection().add( s );
309
310 s = new SourceSectionType();
311 s.setName( DOCUMENTATION_SECTION_NAME );
312 s.setHeadTemplate( IMPLEMENTATION_DOCUMENTATION_TEMPLATE );
313 s.setOptional( true );
314 sourceFileType.getSourceSections().getSourceSection().add( s );
315
316 for ( String interfaceName : this.getJavaInterfaceNames( implementation, false ) )
317 {
318 s = new SourceSectionType();
319 s.setName( interfaceName );
320 s.setIndentationLevel( 1 );
321 s.setOptional( false );
322 s.setEditable( true );
323 sourceFileType.getSourceSections().getSourceSection().add( s );
324 }
325
326 s = new SourceSectionType();
327 s.setName( this.getJavaTypeName( implementation, false ) );
328 s.setIndentationLevel( 1 );
329 s.setOptional( false );
330 s.setEditable( true );
331 sourceFileType.getSourceSections().getSourceSection().add( s );
332
333 s = new SourceSectionType();
334 s.setName( CONSTRUCTORS_SECTION_NAME );
335 s.setIndentationLevel( 1 );
336 s.setHeadTemplate( CONSTRUCTORS_HEAD_TEMPLATE );
337 s.setTailTemplate( CONSTRUCTORS_TAIL_TEMPLATE );
338 s.setOptional( specifications == null || ( specifications.getSpecification().isEmpty()
339 && specifications.getReference().isEmpty() ) );
340
341 s.setSourceSections( new SourceSectionsType() );
342 sourceFileType.getSourceSections().getSourceSection().add( s );
343
344 final SourceSectionType defaultCtor = new SourceSectionType();
345 defaultCtor.setName( DEFAULT_CONSTRUCTOR_SECTION_NAME );
346 defaultCtor.setIndentationLevel( 2 );
347 defaultCtor.setHeadTemplate( DEFAULT_CONSTRUCTOR_TEMPLATE );
348 defaultCtor.setOptional( false );
349 defaultCtor.setEditable( true );
350 s.getSourceSections().getSourceSection().add( defaultCtor );
351
352 s = new SourceSectionType();
353 s.setName( DEPENDENCIES_SECTION_NAME );
354 s.setIndentationLevel( 1 );
355 s.setHeadTemplate( DEPENDENCIES_TEMPLATE );
356 s.setOptional( dependencies == null || dependencies.getDependency().isEmpty() );
357 sourceFileType.getSourceSections().getSourceSection().add( s );
358
359 s = new SourceSectionType();
360 s.setName( PROPERTIES_SECTION_NAME );
361 s.setIndentationLevel( 1 );
362 s.setHeadTemplate( PROPERTIES_TEMPLATE );
363 s.setOptional( properties == null || properties.getProperty().isEmpty() );
364 sourceFileType.getSourceSections().getSourceSection().add( s );
365
366 s = new SourceSectionType();
367 s.setName( MESSAGES_SECTION_NAME );
368 s.setIndentationLevel( 1 );
369 s.setHeadTemplate( MESSAGES_TEMPLATE );
370 s.setOptional( messages == null || messages.getMessage().isEmpty() );
371 sourceFileType.getSourceSections().getSourceSection().add( s );
372 }
373
374 return sourceFileType;
375 }
376
377 /**
378 * Manages the source files of the modules of the instance.
379 *
380 * @param sourcesDirectory The directory holding the source files to manage.
381 *
382 * @throws NullPointerException if {@code sourcesDirectory} is {@code null}.
383 * @throws IOException if managing source files fails.
384 *
385 * @see #manageSourceFiles(org.jomc.model.Module, java.io.File)
386 */
387 public void manageSourceFiles( final File sourcesDirectory ) throws IOException
388 {
389 if ( sourcesDirectory == null )
390 {
391 throw new NullPointerException( "sourcesDirectory" );
392 }
393
394 for ( Module m : this.getModules().getModule() )
395 {
396 this.manageSourceFiles( m, sourcesDirectory );
397 }
398 }
399
400 /**
401 * Manages the source files of a given module of the modules of the instance.
402 *
403 * @param module The module to process.
404 * @param sourcesDirectory The directory holding the source files to manage.
405 *
406 * @throws NullPointerException if {@code module} or {@code sourcesDirectory} is {@code null}.
407 * @throws IOException if managing source files fails.
408 *
409 * @see #manageSourceFiles(org.jomc.model.Specification, java.io.File)
410 * @see #manageSourceFiles(org.jomc.model.Implementation, java.io.File)
411 */
412 public void manageSourceFiles( final Module module, final File sourcesDirectory ) throws IOException
413 {
414 if ( module == null )
415 {
416 throw new NullPointerException( "module" );
417 }
418 if ( sourcesDirectory == null )
419 {
420 throw new NullPointerException( "sourcesDirectory" );
421 }
422
423 assert this.getModules().getModule( module.getName() ) != null : "Module '" + module.getName() + "' not found.";
424
425 if ( module.getSpecifications() != null )
426 {
427 for ( Specification s : module.getSpecifications().getSpecification() )
428 {
429 this.manageSourceFiles( s, sourcesDirectory );
430 }
431 }
432 if ( module.getImplementations() != null )
433 {
434 for ( Implementation i : module.getImplementations().getImplementation() )
435 {
436 this.manageSourceFiles( i, sourcesDirectory );
437 }
438 }
439 }
440
441 /**
442 * Manages the source file of a given specification of the modules of the instance.
443 *
444 * @param specification The specification to process.
445 * @param sourcesDirectory The directory holding the source files to manage.
446 *
447 * @throws NullPointerException if {@code specification} or {@code sourcesDirectory} is {@code null}.
448 * @throws IOException if managing source files fails.
449 *
450 * @see #getSourceFileEditor(org.jomc.model.Specification)
451 */
452 public void manageSourceFiles( final Specification specification, final File sourcesDirectory ) throws IOException
453 {
454 if ( specification == null )
455 {
456 throw new NullPointerException( "specification" );
457 }
458 if ( sourcesDirectory == null )
459 {
460 throw new NullPointerException( "sourcesDirectory" );
461 }
462
463 assert this.getModules().getSpecification( specification.getIdentifier() ) != null :
464 "Specification '" + specification.getIdentifier() + "' not found.";
465
466 final Implementation i = this.getModules().getImplementation( specification.getIdentifier() );
467
468 if ( i != null && i.isClassDeclaration() )
469 {
470 this.manageSourceFiles( i, sourcesDirectory );
471 }
472 else if ( specification.isClassDeclaration() )
473 {
474 this.editSourceFile( sourcesDirectory, this.getSourceFileEditor( specification ) );
475 }
476 }
477
478 /**
479 * Manages the source file of a given implementation of the modules of the instance.
480 *
481 * @param implementation The implementation to process.
482 * @param sourcesDirectory The directory holding the source files to manage.
483 *
484 * @throws NullPointerException if {@code implementation} or {@code sourcesDirectory} is {@code null}.
485 * @throws IOException if managing source files fails.
486 *
487 * @see #getSourceFileEditor(org.jomc.model.Implementation)
488 */
489 public void manageSourceFiles( final Implementation implementation, final File sourcesDirectory )
490 throws IOException
491 {
492 if ( implementation == null )
493 {
494 throw new NullPointerException( "implementation" );
495 }
496 if ( sourcesDirectory == null )
497 {
498 throw new NullPointerException( "sourcesDirectory" );
499 }
500
501 assert this.getModules().getImplementation( implementation.getIdentifier() ) != null :
502 "Implementation '" + implementation.getIdentifier() + "' not found.";
503
504 if ( implementation.isClassDeclaration() )
505 {
506 this.editSourceFile( sourcesDirectory, this.getSourceFileEditor( implementation ) );
507 }
508 }
509
510 /**
511 * Gets a new editor for editing the source file of a given specification of the modules of the instance.
512 *
513 * @param specification The specification whose source file to edit.
514 *
515 * @return A new editor for editing the source file of {@code specification}.
516 *
517 * @throws NullPointerException if {@code specification} is {@code null}.
518 */
519 public SourceFileEditor getSourceFileEditor( final Specification specification )
520 {
521 if ( specification == null )
522 {
523 throw new NullPointerException( "specification" );
524 }
525
526 assert this.getModules().getSpecification( specification.getIdentifier() ) != null :
527 "Specification '" + specification.getIdentifier() + "' not found.";
528
529 return new SourceFileEditor( specification, new TrailingWhitespaceEditor( this.getLineSeparator() ),
530 this.getLineSeparator() );
531
532 }
533
534 /**
535 * Gets a new editor for editing the source file of a given implementation of the modules of the instance.
536 *
537 * @param implementation The implementation whose source file to edit.
538 *
539 * @return A new editor for editing the source file of {@code implementation}.
540 *
541 * @throws NullPointerException if {@code implementation} is {@code null}.
542 */
543 public SourceFileEditor getSourceFileEditor( final Implementation implementation )
544 {
545 if ( implementation == null )
546 {
547 throw new NullPointerException( "implementation" );
548 }
549
550 assert this.getModules().getImplementation( implementation.getIdentifier() ) != null :
551 "Implementation '" + implementation.getIdentifier() + "' not found.";
552
553 return new SourceFileEditor( implementation, new TrailingWhitespaceEditor( this.getLineSeparator() ),
554 this.getLineSeparator() );
555
556 }
557
558 /**
559 * Edits a given file using a given editor.
560 *
561 * @param sourcesDirectory The directory holding the source file to edit.
562 * @param editor The editor to edit {@code f} with.
563 *
564 * @throws NullPointerException if {@code sourcesDirectory} or {@code editor} is {@code null}.
565 * @throws IOException if editing fails.
566 */
567 private void editSourceFile( final File sourcesDirectory, final SourceFileEditor editor ) throws IOException
568 {
569 if ( sourcesDirectory == null )
570 {
571 throw new NullPointerException( "sourcesDirectory" );
572 }
573 if ( editor == null )
574 {
575 throw new NullPointerException( "editor" );
576 }
577
578 final SourceFileType sourceFileType = editor.getSourceFileType();
579 if ( sourceFileType != null )
580 {
581 String content = "";
582 String edited = null;
583 final File f = new File( sourcesDirectory, sourceFileType.getLocation() );
584
585 if ( !f.exists() )
586 {
587 if ( sourceFileType.getTemplate() != null )
588 {
589 if ( this.isLoggable( Level.FINE ) )
590 {
591 this.log( Level.FINE, getMessage( "creating", this.getClass().getName(),
592 f.getAbsolutePath() ), null );
593
594 }
595
596 final StringWriter writer = new StringWriter();
597 final Template template = this.getVelocityTemplate( sourceFileType.getTemplate() );
598 final VelocityContext ctx = editor.getVelocityContext();
599 ctx.put( "template", template );
600 template.merge( ctx, writer );
601 writer.close();
602 content = writer.toString();
603 }
604 }
605 else
606 {
607 if ( this.isLoggable( Level.FINE ) )
608 {
609 this.log( Level.FINE, getMessage( "reading", this.getClass().getName(),
610 f.getAbsolutePath() ), null );
611
612 }
613
614 content = FileUtils.readFileToString( f, this.getInputEncoding() );
615 }
616
617 try
618 {
619 edited = editor.edit( content );
620 }
621 catch ( final IOException e )
622 {
623 throw (IOException) new IOException( getMessage(
624 "failedEditing", f.getAbsolutePath(), e.getMessage() ) ).initCause( e );
625
626 }
627
628 if ( this.isLoggable( Level.FINE ) )
629 {
630 for ( Section s : editor.getAddedSections() )
631 {
632 this.log( Level.FINE, getMessage( "addedSection", this.getClass().getName(), f.getAbsolutePath(),
633 s.getName() ), null );
634
635 }
636 }
637
638 if ( this.isLoggable( Level.WARNING ) )
639 {
640 for ( Section s : editor.getUnknownSections() )
641 {
642 this.log( Level.WARNING, getMessage( "unknownSection", f.getAbsolutePath(), s.getName() ), null );
643 }
644 }
645
646 if ( !edited.equals( content ) || edited.length() == 0 )
647 {
648 if ( !f.getParentFile().exists() && !f.getParentFile().mkdirs() )
649 {
650 throw new IOException( getMessage(
651 "failedCreatingDirectory", f.getParentFile().getAbsolutePath() ) );
652
653 }
654
655 if ( this.isLoggable( Level.INFO ) )
656 {
657 this.log( Level.INFO, getMessage( "editing", f.getAbsolutePath() ), null );
658 }
659
660 FileUtils.writeStringToFile( f, edited, this.getOutputEncoding() );
661 }
662 else if ( this.isLoggable( Level.FINE ) )
663 {
664 this.log( Level.FINE, getMessage( "unchanged", this.getClass().getName(), f.getAbsolutePath() ), null );
665 }
666 }
667 }
668
669 private static String getMessage( final String key, final Object... arguments )
670 {
671 if ( key == null )
672 {
673 throw new NullPointerException( "key" );
674 }
675
676 return MessageFormat.format( ResourceBundle.getBundle(
677 SourceFileProcessor.class.getName().replace( '.', '/' ) ).getString( key ), arguments );
678
679 }
680
681 /**
682 * Extension to {@code SectionEditor} adding support for editing source code files.
683 *
684 * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
685 * @version $Id: SourceFileProcessor.java 2164 2010-06-28 03:25:51Z schulte2005 $
686 */
687 public class SourceFileEditor extends SectionEditor
688 {
689
690 /** {@code Specification} of the instance or {@code null}. */
691 private final Specification specification;
692
693 /** {@code Implementation} of the instance or {@code null}. */
694 private final Implementation implementation;
695
696 /** List of sections added to the input. */
697 private List<Section> addedSections;
698
699 /** List of sections without corresponding model entry. */
700 private List<Section> unknownSections;
701
702 /**
703 * Creates a new {@code SourceFileEditor} taking a {@code Specification} to edit source code of.
704 *
705 * @param specification The specification to edit source code of.
706 */
707 public SourceFileEditor( final Specification specification )
708 {
709 this( specification, null, null );
710 }
711
712 /**
713 * Creates a new {@code SourceFileEditor} taking a {@code Specification} to edit source code of and a line
714 * separator.
715 *
716 * @param specification The specification to edit source code of.
717 * @param lineSeparator The line separator of the editor.
718 */
719 public SourceFileEditor( final Specification specification, final String lineSeparator )
720 {
721 this( specification, null, lineSeparator );
722 }
723
724 /**
725 * Creates a new {@code SourceFileEditor} taking a {@code Specification} to edit source code of and an editor to
726 * chain.
727 *
728 * @param specification The specification backing the editor.
729 * @param lineEditor The editor to chain.
730 */
731 public SourceFileEditor( final Specification specification, final LineEditor lineEditor )
732 {
733 this( specification, lineEditor, null );
734 }
735
736 /**
737 * Creates a new {@code SourceFileEditor} taking a {@code Specification} to edit source code of, an editor to
738 * chain and a line separator.
739 *
740 * @param specification The specification backing the editor.
741 * @param lineEditor The editor to chain.
742 * @param lineSeparator The line separator of the editor.
743 */
744 public SourceFileEditor( final Specification specification, final LineEditor lineEditor,
745 final String lineSeparator )
746 {
747 super( lineEditor, lineSeparator );
748 this.specification = specification;
749 this.implementation = null;
750
751 assert getModules().getSpecification( specification.getIdentifier() ) != null :
752 "Specification '" + specification.getIdentifier() + "' not found.";
753
754 }
755
756 /**
757 * Creates a new {@code SourceFileEditor} taking an {@code Implementation} to edit source code of.
758 *
759 * @param implementation The implementation to edit source code of.
760 */
761 public SourceFileEditor( final Implementation implementation )
762 {
763 this( implementation, null, null );
764 }
765
766 /**
767 * Creates a new {@code SourceFileEditor} taking an {@code Implementation} to edit source code of and a line
768 * separator.
769 *
770 * @param implementation The implementation to edit source code of.
771 * @param lineSeparator The line separator of the editor.
772 */
773 public SourceFileEditor( final Implementation implementation, final String lineSeparator )
774 {
775 this( implementation, null, lineSeparator );
776 }
777
778 /**
779 * Creates a new {@code SourceFileEditor} taking an {@code Implementation} to edit source code of and an editor
780 * to chain.
781 *
782 * @param implementation The implementation to edit source code of.
783 * @param lineEditor The editor to chain.
784 */
785 public SourceFileEditor( final Implementation implementation, final LineEditor lineEditor )
786 {
787 this( implementation, lineEditor, null );
788 }
789
790 /**
791 * Creates a new {@code SourceFileEditor} taking an {@code Implementation} to edit source code of, an editor
792 * to chain and a line separator.
793 *
794 * @param implementation The implementation to edit source code of.
795 * @param lineEditor The editor to chain.
796 * @param lineSeparator The line separator of the editor.
797 */
798 public SourceFileEditor( final Implementation implementation, final LineEditor lineEditor,
799 final String lineSeparator )
800 {
801 super( lineEditor, lineSeparator );
802 this.implementation = implementation;
803 this.specification = null;
804
805 assert getModules().getImplementation( implementation.getIdentifier() ) != null :
806 "Implementation '" + implementation.getIdentifier() + "' not found.";
807
808 }
809
810 /**
811 * Gets a list of sections added to the input.
812 *
813 * @return A list of sections added to the input.
814 */
815 public List<Section> getAddedSections()
816 {
817 if ( this.addedSections == null )
818 {
819 this.addedSections = new LinkedList<Section>();
820 }
821
822 return this.addedSections;
823 }
824
825 /**
826 * Gets a list of sections without corresponding model entry.
827 *
828 * @return A list of sections without corresponding model entry.
829 */
830 public List<Section> getUnknownSections()
831 {
832 if ( this.unknownSections == null )
833 {
834 this.unknownSections = new LinkedList<Section>();
835 }
836
837 return this.unknownSections;
838 }
839
840 /**
841 * Gets the model of the editor.
842 *
843 * @return The model of the editor.
844 */
845 protected SourceFileType getSourceFileType()
846 {
847 if ( this.specification != null )
848 {
849 return SourceFileProcessor.this.getSourceFileType( this.specification );
850 }
851
852 if ( this.implementation != null )
853 {
854 return SourceFileProcessor.this.getSourceFileType( this.implementation );
855 }
856
857 return null;
858 }
859
860 /**
861 * Gets the velocity context used for merging templates.
862 *
863 * @return The velocity context used for merging templates.
864 */
865 protected VelocityContext getVelocityContext()
866 {
867 final VelocityContext ctx = SourceFileProcessor.this.getVelocityContext();
868
869 if ( this.specification != null )
870 {
871 ctx.put( "specification", this.specification );
872 }
873
874 if ( this.implementation != null )
875 {
876 ctx.put( "implementation", this.implementation );
877 }
878
879 return ctx;
880 }
881
882 /**
883 * {@inheritDoc}
884 * <p>This method creates any sections declared in the model of the editor as returned by method
885 * {@code getSourceFileType} prior to rendering the output of the editor.</p>
886 *
887 * @param section The section to start rendering the editor's output with.
888 *
889 * @see #getSourceFileType()
890 */
891 @Override
892 protected String getOutput( final Section section ) throws IOException
893 {
894 this.getAddedSections().clear();
895 this.getUnknownSections().clear();
896
897 final SourceFileType sourceFileType = this.getSourceFileType();
898
899 if ( sourceFileType != null )
900 {
901 this.createSections( sourceFileType.getSourceSections(), section );
902 }
903
904 return super.getOutput( section );
905 }
906
907 /**
908 * {@inheritDoc}
909 * <p>This method searches the model of the editor for a section matching {@code s} and updates properties
910 * {@code headContent} and {@code tailContent} of {@code s} according to the templates declared in the model
911 * as returned by method {@code getSourceFileType}.</p>
912 *
913 * @param s The section to edit.
914 *
915 * @see #getSourceFileType()
916 */
917 @Override
918 protected void editSection( final Section s ) throws IOException
919 {
920 super.editSection( s );
921
922 final SourceFileType sourceFileType = this.getSourceFileType();
923
924 if ( s.getName() != null && sourceFileType != null && sourceFileType.getSourceSections() != null )
925 {
926 final SourceSectionType sourceSectionType =
927 sourceFileType.getSourceSections().getSourceSection( s.getName() );
928
929 if ( sourceSectionType != null )
930 {
931 if ( s.getStartingLine() != null )
932 {
933 s.setStartingLine( getIndentation( sourceSectionType.getIndentationLevel() )
934 + s.getStartingLine().trim() );
935
936 }
937 if ( s.getEndingLine() != null )
938 {
939 s.setEndingLine( getIndentation( sourceSectionType.getIndentationLevel() )
940 + s.getEndingLine().trim() );
941
942 }
943
944 if ( sourceSectionType.getHeadTemplate() != null
945 && ( !sourceSectionType.isEditable() || s.getHeadContent().toString().trim().length() == 0 ) )
946 {
947 final StringWriter writer = new StringWriter();
948 final Template template = getVelocityTemplate( sourceSectionType.getHeadTemplate() );
949 final VelocityContext ctx = getVelocityContext();
950 ctx.put( "template", template );
951 template.merge( ctx, writer );
952 writer.close();
953 s.getHeadContent().setLength( 0 );
954 s.getHeadContent().append( writer.toString() );
955 }
956
957 if ( sourceSectionType.getTailTemplate() != null
958 && ( !sourceSectionType.isEditable() || s.getTailContent().toString().trim().length() == 0 ) )
959 {
960 final StringWriter writer = new StringWriter();
961 final Template template = getVelocityTemplate( sourceSectionType.getTailTemplate() );
962 final VelocityContext ctx = getVelocityContext();
963 ctx.put( "template", template );
964 template.merge( ctx, writer );
965 writer.close();
966 s.getTailContent().setLength( 0 );
967 s.getTailContent().append( writer.toString() );
968 }
969 }
970 else
971 {
972 this.getUnknownSections().add( s );
973 }
974 }
975 }
976
977 private void createSections( final SourceSectionsType sourceSectionsType, final Section section )
978 {
979 if ( sourceSectionsType != null && section != null )
980 {
981 for ( SourceSectionType sourceSectionType : sourceSectionsType.getSourceSection() )
982 {
983 Section childSection = section.getSection( sourceSectionType.getName() );
984
985 if ( childSection == null && !sourceSectionType.isOptional() )
986 {
987 childSection = new Section();
988 childSection.setName( sourceSectionType.getName() );
989 childSection.setStartingLine( getIndentation( sourceSectionType.getIndentationLevel() )
990 + "// SECTION-START[" + sourceSectionType.getName() + "]" );
991
992 childSection.setEndingLine( getIndentation( sourceSectionType.getIndentationLevel() )
993 + "// SECTION-END" );
994
995 section.getSections().add( childSection );
996 this.getAddedSections().add( childSection );
997 }
998
999 this.createSections( sourceSectionType.getSourceSections(), childSection );
1000 }
1001 }
1002 }
1003
1004 }
1005 }