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 4265 2012-02-16 21:17:26Z 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 4265 2012-02-16 21:17:26Z 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 4265 2012-02-16 21:17:26Z 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 /** List of sections added to the input. */
575 @Deprecated
576 private List<Section> addedSections;
577
578 /** List of sections without corresponding model entry. */
579 @Deprecated
580 private List<Section> unknownSections;
581
582 /**
583 * Creates a new {@code SourceFileEditor} instance.
584 *
585 * @since 1.2
586 */
587 public SourceFileEditor()
588 {
589 this( (LineEditor) null, (String) null );
590 }
591
592 /**
593 * Creates a new {@code SourceFileEditor} instance taking a string to use for separating lines.
594 *
595 * @param lineSeparator String to use for separating lines.
596 *
597 * @since 1.2
598 */
599 public SourceFileEditor( final String lineSeparator )
600 {
601 this( (LineEditor) null, lineSeparator );
602 }
603
604 /**
605 * Creates a new {@code SourceFileEditor} instance taking an editor to chain.
606 *
607 * @param editor The editor to chain.
608 *
609 * @since 1.2
610 */
611 public SourceFileEditor( final LineEditor editor )
612 {
613 this( editor, null );
614 }
615
616 /**
617 * Creates a new {@code SourceFileEditor} instance taking an editor to chain and a string to use for separating
618 * lines.
619 *
620 * @param editor The editor to chain.
621 * @param lineSeparator String to use for separating lines.
622 *
623 * @since 1.2
624 */
625 public SourceFileEditor( final LineEditor editor, final String lineSeparator )
626 {
627 super( editor, lineSeparator );
628 }
629
630 /**
631 * Creates a new {@code SourceFileEditor} taking a {@code Specification} to edit source code of.
632 *
633 * @param specification The specification to edit source code of.
634 *
635 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Specification, org.jomc.tools.model.SourceFileType, java.io.File)}.
636 * This constructor will be removed in version 2.0.
637 */
638 @Deprecated
639 public SourceFileEditor( final Specification specification )
640 {
641 this( specification, null, null );
642 }
643
644 /**
645 * Creates a new {@code SourceFileEditor} taking a {@code Specification} to edit source code of and a line
646 * separator.
647 *
648 * @param specification The specification to edit source code of.
649 * @param lineSeparator The line separator of the editor.
650 *
651 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Specification, org.jomc.tools.model.SourceFileType, java.io.File)}.
652 * This constructor will be removed in version 2.0.
653 */
654 @Deprecated
655 public SourceFileEditor( final Specification specification, final String lineSeparator )
656 {
657 this( specification, null, lineSeparator );
658 }
659
660 /**
661 * Creates a new {@code SourceFileEditor} taking a {@code Specification} to edit source code of and an editor to
662 * chain.
663 *
664 * @param specification The specification backing the editor.
665 * @param lineEditor The editor to chain.
666 *
667 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Specification, org.jomc.tools.model.SourceFileType, java.io.File)}.
668 * This constructor will be removed in version 2.0.
669 */
670 @Deprecated
671 public SourceFileEditor( final Specification specification, final LineEditor lineEditor )
672 {
673 this( specification, lineEditor, null );
674 }
675
676 /**
677 * Creates a new {@code SourceFileEditor} taking a {@code Specification} to edit source code of, an editor to
678 * chain and a line separator.
679 *
680 * @param specification The specification backing the editor.
681 * @param lineEditor The editor to chain.
682 * @param lineSeparator The line separator of the editor.
683 *
684 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Specification, org.jomc.tools.model.SourceFileType, java.io.File)}.
685 * This constructor will be removed in version 2.0.
686 */
687 @Deprecated
688 public SourceFileEditor( final Specification specification, final LineEditor lineEditor,
689 final String lineSeparator )
690 {
691 super( lineEditor, lineSeparator );
692 this.specification = specification;
693 this.implementation = null;
694
695 assert getModules().getSpecification( specification.getIdentifier() ) != null :
696 "Specification '" + specification.getIdentifier() + "' not found.";
697
698 }
699
700 /**
701 * Creates a new {@code SourceFileEditor} taking an {@code Implementation} to edit source code of.
702 *
703 * @param implementation The implementation to edit source code of.
704 *
705 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Implementation, org.jomc.tools.model.SourceFileType, java.io.File)}.
706 * This constructor will be removed in version 2.0.
707 */
708 @Deprecated
709 public SourceFileEditor( final Implementation implementation )
710 {
711 this( implementation, null, null );
712 }
713
714 /**
715 * Creates a new {@code SourceFileEditor} taking an {@code Implementation} to edit source code of and a line
716 * separator.
717 *
718 * @param implementation The implementation to edit source code of.
719 * @param lineSeparator The line separator of the editor.
720 *
721 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Implementation, org.jomc.tools.model.SourceFileType, java.io.File)}.
722 * This constructor will be removed in version 2.0.
723 */
724 @Deprecated
725 public SourceFileEditor( final Implementation implementation, final String lineSeparator )
726 {
727 this( implementation, null, lineSeparator );
728 }
729
730 /**
731 * Creates a new {@code SourceFileEditor} taking an {@code Implementation} to edit source code of and an editor
732 * to chain.
733 *
734 * @param implementation The implementation to edit source code of.
735 * @param lineEditor The editor to chain.
736 *
737 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Implementation, org.jomc.tools.model.SourceFileType, java.io.File)}.
738 * This constructor will be removed in version 2.0.
739 */
740 @Deprecated
741 public SourceFileEditor( final Implementation implementation, final LineEditor lineEditor )
742 {
743 this( implementation, lineEditor, null );
744 }
745
746 /**
747 * Creates a new {@code SourceFileEditor} taking an {@code Implementation} to edit source code of, an editor
748 * to chain and a line separator.
749 *
750 * @param implementation The implementation to edit source code of.
751 * @param lineEditor The editor to chain.
752 * @param lineSeparator The line separator of the editor.
753 *
754 * @deprecated As of JOMC 1.2, please use method {@link #edit(org.jomc.model.Implementation, org.jomc.tools.model.SourceFileType, java.io.File)}.
755 * This constructor will be removed in version 2.0.
756 */
757 @Deprecated
758 public SourceFileEditor( final Implementation implementation, final LineEditor lineEditor,
759 final String lineSeparator )
760 {
761 super( lineEditor, lineSeparator );
762 this.implementation = implementation;
763 this.specification = null;
764
765 assert getModules().getImplementation( implementation.getIdentifier() ) != null :
766 "Implementation '" + implementation.getIdentifier() + "' not found.";
767
768 }
769
770 /**
771 * Edits a source file of a given specification.
772 *
773 * @param specification The specification to edit a source file of.
774 * @param sourceFileType The model of the source file to edit.
775 * @param sourcesDirectory The directory holding the source file to edit.
776 *
777 * @throws NullPointerException if {@code specification}, {@code sourceFileType} or {@code sourcesDirectory} is
778 * {@code null}.
779 * @throws IOException if editing fails.
780 *
781 * @since 1.2
782 */
783 public final void edit( final Specification specification, final SourceFileType sourceFileType,
784 final File sourcesDirectory ) throws IOException
785 {
786 if ( specification == null )
787 {
788 throw new NullPointerException( "specification" );
789 }
790 if ( sourceFileType == null )
791 {
792 throw new NullPointerException( "sourceFileType" );
793 }
794 if ( sourcesDirectory == null )
795 {
796 throw new NullPointerException( "sourcesDirectory" );
797 }
798
799 assert getModules().getSpecification( specification.getIdentifier() ) != null :
800 "Specification '" + specification.getIdentifier() + "' not found.";
801
802 this.specification = specification;
803 this.sourceFileType = sourceFileType;
804 this.editSourceFile( sourcesDirectory );
805 }
806
807 /**
808 * Edits a source file of a given implementation.
809 *
810 * @param implementation The implementation to edit a source file of.
811 * @param sourceFileType The model of the source file to edit.
812 * @param sourcesDirectory The directory holding the source file to edit.
813 *
814 * @throws NullPointerException if {@code implementation}, {@code sourceFileType} or {@code sourcesDirectory} is
815 * {@code null}.
816 * @throws IOException if editing fails.
817 *
818 * @since 1.2
819 */
820 public final void edit( final Implementation implementation, final SourceFileType sourceFileType,
821 final File sourcesDirectory ) throws IOException
822 {
823 if ( implementation == null )
824 {
825 throw new NullPointerException( "implementation" );
826 }
827 if ( sourceFileType == null )
828 {
829 throw new NullPointerException( "sourceFileType" );
830 }
831 if ( sourcesDirectory == null )
832 {
833 throw new NullPointerException( "sourcesDirectory" );
834 }
835
836 assert getModules().getImplementation( implementation.getIdentifier() ) != null :
837 "Implementation '" + implementation.getIdentifier() + "' not found.";
838
839 this.implementation = implementation;
840 this.sourceFileType = sourceFileType;
841 this.editSourceFile( sourcesDirectory );
842 }
843
844 /**
845 * Gets a list of sections added to the input.
846 * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you
847 * make to the returned list will be present inside the object. This is why there is no {@code set} method
848 * for the added sections property.</p>
849 *
850 * @return A list of sections added to the input.
851 *
852 * @deprecated As of JOMC 1.2, deprecated without replacement. This method will be removed in version 2.0.
853 */
854 @Deprecated
855 public List<Section> getAddedSections()
856 {
857 if ( this.addedSections == null )
858 {
859 this.addedSections = new LinkedList<Section>();
860 }
861
862 return this.addedSections;
863 }
864
865 /**
866 * Gets a list of sections without corresponding model entry.
867 * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you
868 * make to the returned list will be present inside the object. This is why there is no {@code set} method
869 * for the unknown sections property.</p>
870 *
871 * @return A list of sections without corresponding model entry.
872 *
873 * @deprecated As of JOMC 1.2, deprecated without replacement. This method will be removed in version 2.0.
874 */
875 @Deprecated
876 public List<Section> getUnknownSections()
877 {
878 if ( this.unknownSections == null )
879 {
880 this.unknownSections = new LinkedList<Section>();
881 }
882
883 return this.unknownSections;
884 }
885
886 /**
887 * Gets the currently edited source code file.
888 *
889 * @return The currently edited source code file.
890 *
891 * @deprecated As of JOMC 1.2, deprecated without replacement. This method will be removed in version 2.0.
892 */
893 @Deprecated
894 protected SourceFileType getSourceFileType()
895 {
896 if ( this.sourceFileType == null )
897 {
898 if ( this.specification != null )
899 {
900 return SourceFileProcessor.this.getSourceFileType( this.specification );
901 }
902
903 if ( this.implementation != null )
904 {
905 return SourceFileProcessor.this.getSourceFileType( this.implementation );
906 }
907 }
908
909 return this.sourceFileType;
910 }
911
912 /**
913 * Gets a new velocity context used for merging templates.
914 *
915 * @return A new velocity context used for merging templates.
916 *
917 * @deprecated As of JOMC 1.2, deprecated without replacement. This method will be removed in version 2.0.
918 */
919 @Deprecated
920 protected VelocityContext getVelocityContext()
921 {
922 final VelocityContext ctx = SourceFileProcessor.this.getVelocityContext();
923
924 if ( this.specification != null )
925 {
926 ctx.put( "specification", this.specification );
927 }
928
929 if ( this.implementation != null )
930 {
931 ctx.put( "implementation", this.implementation );
932 }
933
934 return ctx;
935 }
936
937 /**
938 * {@inheritDoc}
939 * <p>This method creates any sections declared in the model of the source file as returned by method
940 * {@code getSourceFileType} prior to rendering the output of the editor.</p>
941 *
942 * @param section The section to start rendering the editor's output with.
943 *
944 * @see #createSection(java.lang.String, java.lang.String, org.jomc.tools.model.SourceSectionType)
945 */
946 @Override
947 protected String getOutput( final Section section ) throws IOException
948 {
949 this.getAddedSections().clear();
950 this.getUnknownSections().clear();
951
952 final SourceFileType model = this.getSourceFileType();
953
954 if ( model != null )
955 {
956 this.createSections( model, model.getSourceSections(), section );
957 }
958
959 return super.getOutput( section );
960 }
961
962 /**
963 * {@inheritDoc}
964 * <p>This method searches the model of the source file for a section matching {@code s} and updates properties
965 * {@code headContent} and {@code tailContent} of {@code s} according to the templates declared in the model
966 * as returned by method {@code getSourceFileType}.</p>
967 *
968 * @param s The section to edit.
969 */
970 @Override
971 protected void editSection( final Section s ) throws IOException
972 {
973 try
974 {
975 super.editSection( s );
976
977 final SourceFileType model = this.getSourceFileType();
978
979 if ( s.getName() != null && model != null && model.getSourceSections() != null )
980 {
981 final SourceSectionType sourceSectionType =
982 model.getSourceSections().getSourceSection( s.getName() );
983
984 if ( sourceSectionType != null )
985 {
986 if ( s.getStartingLine() != null )
987 {
988 s.setStartingLine( getIndentation( sourceSectionType.getIndentationLevel() )
989 + s.getStartingLine().trim() );
990
991 }
992 if ( s.getEndingLine() != null )
993 {
994 s.setEndingLine( getIndentation( sourceSectionType.getIndentationLevel() )
995 + s.getEndingLine().trim() );
996
997 }
998
999 if ( sourceSectionType.getHeadTemplate() != null
1000 && ( !sourceSectionType.isEditable()
1001 || s.getHeadContent().toString().trim().length() == 0 ) )
1002 {
1003 final StringWriter writer = new StringWriter();
1004 final Template template = getVelocityTemplate( sourceSectionType.getHeadTemplate() );
1005 final VelocityContext ctx = getVelocityContext();
1006 ctx.put( "template", template );
1007 template.merge( ctx, writer );
1008 writer.close();
1009 s.getHeadContent().setLength( 0 );
1010 s.getHeadContent().append( writer.toString() );
1011 }
1012
1013 if ( sourceSectionType.getTailTemplate() != null
1014 && ( !sourceSectionType.isEditable()
1015 || s.getTailContent().toString().trim().length() == 0 ) )
1016 {
1017 final StringWriter writer = new StringWriter();
1018 final Template template = getVelocityTemplate( sourceSectionType.getTailTemplate() );
1019 final VelocityContext ctx = getVelocityContext();
1020 ctx.put( "template", template );
1021 template.merge( ctx, writer );
1022 writer.close();
1023 s.getTailContent().setLength( 0 );
1024 s.getTailContent().append( writer.toString() );
1025 }
1026 }
1027 else
1028 {
1029 if ( isLoggable( Level.WARNING ) )
1030 {
1031 if ( this.implementation != null )
1032 {
1033 log( Level.WARNING, getMessage(
1034 "unknownImplementationSection", this.implementation.getIdentifier(),
1035 model.getIdentifier(), s.getName() ), null );
1036
1037
1038 }
1039 else if ( this.specification != null )
1040 {
1041 log( Level.WARNING, getMessage(
1042 "unknownSpecificationSection", this.specification.getIdentifier(),
1043 model.getIdentifier(), s.getName() ), null );
1044
1045 }
1046 }
1047
1048 this.getUnknownSections().add( s );
1049 }
1050 }
1051 }
1052 catch ( final VelocityException e )
1053 {
1054 // JDK: As of JDK 6, "new IOException( message, cause )".
1055 throw (IOException) new IOException( getMessage( e ) ).initCause( e );
1056 }
1057 }
1058
1059 private void createSections( final SourceFileType sourceFileType, final SourceSectionsType sourceSectionsType,
1060 final Section section ) throws IOException
1061 {
1062 if ( sourceSectionsType != null && section != null )
1063 {
1064 for ( int i = 0, s0 = sourceSectionsType.getSourceSection().size(); i < s0; i++ )
1065 {
1066 final SourceSectionType sourceSectionType = sourceSectionsType.getSourceSection().get( i );
1067 Section childSection = section.getSection( sourceSectionType.getName() );
1068
1069 if ( childSection == null && !sourceSectionType.isOptional() )
1070 {
1071 childSection = this.createSection( StringUtils.defaultString( sourceFileType.getHeadComment() ),
1072 StringUtils.defaultString( sourceFileType.getTailComment() ),
1073 sourceSectionType );
1074
1075 section.getSections().add( childSection );
1076
1077 if ( isLoggable( Level.FINE ) )
1078 {
1079 log( Level.FINE, getMessage(
1080 "addedSection", sourceFileType.getIdentifier(), childSection.getName() ), null );
1081
1082 }
1083
1084 this.getAddedSections().add( childSection );
1085 }
1086
1087 this.createSections( sourceFileType, sourceSectionType.getSourceSections(), childSection );
1088 }
1089 }
1090 }
1091
1092 /**
1093 * Creates a new {@code Section} instance for a given {@code SourceSectionType}.
1094 *
1095 * @param headComment Characters to use to start a comment in the source file.
1096 * @param tailComment Characters to use to end a comment in the source file.
1097 * @param sourceSectionType The {@code SourceSectionType} to create a new {@code Section} instance for.
1098 *
1099 * @return A new {@code Section} instance for {@code sourceSectionType}.
1100 *
1101 * @throws NullPointerException if {@code headComment}, {@code tailComment} or {@code sourceSectionType} is
1102 * {@code null}.
1103 * @throws IOException if creating a new {@code Section} instance fails.
1104 *
1105 * @since 1.2
1106 */
1107 private Section createSection( final String headComment, final String tailComment,
1108 final SourceSectionType sourceSectionType ) throws IOException
1109 {
1110 if ( headComment == null )
1111 {
1112 throw new NullPointerException( "headComment" );
1113 }
1114 if ( tailComment == null )
1115 {
1116 throw new NullPointerException( "tailComment" );
1117 }
1118 if ( sourceSectionType == null )
1119 {
1120 throw new NullPointerException( "sourceSectionType" );
1121 }
1122
1123 final Section s = new Section();
1124 s.setName( sourceSectionType.getName() );
1125
1126 final StringBuilder head = new StringBuilder( 255 );
1127 head.append( getIndentation( sourceSectionType.getIndentationLevel() ) ).append( headComment );
1128
1129 s.setStartingLine( head + " SECTION-START[" + sourceSectionType.getName() + ']' + tailComment );
1130 s.setEndingLine( head + " SECTION-END" + tailComment );
1131
1132 return s;
1133 }
1134
1135 private void editSourceFile( final File sourcesDirectory ) throws IOException
1136 {
1137 if ( sourcesDirectory == null )
1138 {
1139 throw new NullPointerException( "sourcesDirectory" );
1140 }
1141 if ( !sourcesDirectory.isDirectory() )
1142 {
1143 throw new IOException( getMessage( "directoryNotFound", sourcesDirectory.getAbsolutePath() ) );
1144 }
1145
1146 final SourceFileType model = this.getSourceFileType();
1147
1148 if ( model != null && model.getLocation() != null )
1149 {
1150 final File f = new File( sourcesDirectory, model.getLocation() );
1151
1152 try
1153 {
1154 String content = "";
1155 String edited = null;
1156 boolean creating = false;
1157
1158 if ( !f.exists() )
1159 {
1160 if ( model.getTemplate() != null )
1161 {
1162 final StringWriter writer = new StringWriter();
1163 final Template template = getVelocityTemplate( model.getTemplate() );
1164 final VelocityContext ctx = this.getVelocityContext();
1165 ctx.put( "template", template );
1166 template.merge( ctx, writer );
1167 writer.close();
1168 content = writer.toString();
1169 creating = true;
1170 }
1171 }
1172 else
1173 {
1174 if ( isLoggable( Level.FINER ) )
1175 {
1176 log( Level.FINER, getMessage( "reading", f.getAbsolutePath() ), null );
1177 }
1178
1179 content = this.readSourceFile( f );
1180 }
1181
1182 try
1183 {
1184 edited = super.edit( content );
1185 }
1186 catch ( final IOException e )
1187 {
1188 // JDK: As of JDK 6, "new IOException( message, cause )".
1189 throw (IOException) new IOException( getMessage(
1190 "failedEditing", f.getAbsolutePath(), getMessage( e ) ) ).initCause( e );
1191
1192 }
1193
1194 if ( !edited.equals( content ) || edited.length() == 0 )
1195 {
1196 if ( !f.getParentFile().exists() && !f.getParentFile().mkdirs() )
1197 {
1198 throw new IOException( getMessage(
1199 "failedCreatingDirectory", f.getParentFile().getAbsolutePath() ) );
1200
1201 }
1202
1203 if ( isLoggable( Level.INFO ) )
1204 {
1205 log( Level.INFO, getMessage(
1206 creating ? "creating" : "editing", f.getAbsolutePath() ), null );
1207
1208 }
1209
1210 this.writeSourceFile( f, edited );
1211 }
1212 else if ( isLoggable( Level.FINER ) )
1213 {
1214 log( Level.FINER, getMessage( "unchanged", f.getAbsolutePath() ), null );
1215 }
1216 }
1217 catch ( final VelocityException e )
1218 {
1219 // JDK: As of JDK 6, "new IOException( message, cause )".
1220 throw (IOException) new IOException( getMessage(
1221 "failedEditing", f.getAbsolutePath(), getMessage( e ) ) ).initCause( e );
1222
1223 }
1224 }
1225 }
1226
1227 private String readSourceFile( final File file ) throws IOException
1228 {
1229 if ( file == null )
1230 {
1231 throw new NullPointerException( "file" );
1232 }
1233
1234 RandomAccessFile randomAccessFile = null;
1235 FileChannel fileChannel = null;
1236 FileLock fileLock = null;
1237 boolean suppressExceptionOnClose = true;
1238
1239 //final Charset charset = Charset.forName( getInputEncoding() );
1240 final int length = file.length() > 0L ? Long.valueOf( file.length() ).intValue() : 1;
1241 final ByteBuffer buf = ByteBuffer.allocate( length );
1242 final StringBuilder appendable = new StringBuilder( length );
1243
1244 try
1245 {
1246 randomAccessFile = new RandomAccessFile( file, "r" );
1247 fileChannel = randomAccessFile.getChannel();
1248 fileLock = fileChannel.lock( 0L, file.length(), true );
1249 fileChannel.position( 0L );
1250
1251 buf.clear();
1252 int read = fileChannel.read( buf );
1253
1254 while ( read != -1 )
1255 {
1256 // JDK: As of JDK 6, new String( byte[], int, int, Charset )
1257 appendable.append( new String( buf.array(), buf.arrayOffset(), read, getInputEncoding() ) );
1258 buf.clear();
1259 read = fileChannel.read( buf );
1260 }
1261
1262 suppressExceptionOnClose = false;
1263 return appendable.toString();
1264 }
1265 finally
1266 {
1267 try
1268 {
1269 if ( fileLock != null )
1270 {
1271 fileLock.release();
1272 }
1273 }
1274 catch ( final IOException e )
1275 {
1276 if ( suppressExceptionOnClose )
1277 {
1278 log( Level.SEVERE, null, e );
1279 }
1280 else
1281 {
1282 throw e;
1283 }
1284 }
1285 finally
1286 {
1287 try
1288 {
1289 if ( fileChannel != null )
1290 {
1291 fileChannel.close();
1292 }
1293 }
1294 catch ( final IOException e )
1295 {
1296 if ( suppressExceptionOnClose )
1297 {
1298 log( Level.SEVERE, null, e );
1299 }
1300 else
1301 {
1302 throw e;
1303 }
1304 }
1305 finally
1306 {
1307 try
1308 {
1309 if ( randomAccessFile != null )
1310 {
1311 randomAccessFile.close();
1312 }
1313 }
1314 catch ( final IOException e )
1315 {
1316 if ( suppressExceptionOnClose )
1317 {
1318 log( Level.SEVERE, null, e );
1319 }
1320 else
1321 {
1322 throw e;
1323 }
1324 }
1325 }
1326 }
1327 }
1328 }
1329
1330 private void writeSourceFile( final File file, final String content ) throws IOException
1331 {
1332 if ( file == null )
1333 {
1334 throw new NullPointerException( "file" );
1335 }
1336 if ( content == null )
1337 {
1338 throw new NullPointerException( "content" );
1339 }
1340
1341 RandomAccessFile randomAccessFile = null;
1342 FileChannel fileChannel = null;
1343 FileLock fileLock = null;
1344 boolean suppressExceptionOnClose = true;
1345 final byte[] bytes = content.getBytes( getOutputEncoding() );
1346
1347 try
1348 {
1349 randomAccessFile = new RandomAccessFile( file, "rw" );
1350 fileChannel = randomAccessFile.getChannel();
1351 fileLock = fileChannel.lock( 0L, bytes.length, false );
1352 fileChannel.truncate( bytes.length );
1353 fileChannel.position( 0L );
1354 fileChannel.write( ByteBuffer.wrap( bytes ) );
1355 fileChannel.force( true );
1356 suppressExceptionOnClose = false;
1357 }
1358 finally
1359 {
1360 try
1361 {
1362 if ( fileLock != null )
1363 {
1364 fileLock.release();
1365 }
1366 }
1367 catch ( final IOException e )
1368 {
1369 if ( suppressExceptionOnClose )
1370 {
1371 log( Level.SEVERE, null, e );
1372 }
1373 else
1374 {
1375 throw e;
1376 }
1377 }
1378 finally
1379 {
1380 try
1381 {
1382 if ( fileChannel != null )
1383 {
1384 fileChannel.close();
1385 }
1386 }
1387 catch ( final IOException e )
1388 {
1389 if ( suppressExceptionOnClose )
1390 {
1391 log( Level.SEVERE, null, e );
1392 }
1393 else
1394 {
1395 throw e;
1396 }
1397 }
1398 finally
1399 {
1400 try
1401 {
1402 if ( randomAccessFile != null )
1403 {
1404 randomAccessFile.close();
1405 }
1406 }
1407 catch ( final IOException e )
1408 {
1409 if ( suppressExceptionOnClose )
1410 {
1411 log( Level.SEVERE, null, e );
1412 }
1413 else
1414 {
1415 throw e;
1416 }
1417 }
1418 }
1419 }
1420 }
1421 }
1422
1423 }
1424
1425 }