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