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