View Javadoc
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 }