View Javadoc

1   /*
2    *   Copyright (C) Christian Schulte, 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: ClassFileProcessor.java 4613 2012-09-22 10:07:08Z schulte $
29   *
30   */
31  package org.jomc.tools;
32  
33  import java.io.ByteArrayInputStream;
34  import java.io.ByteArrayOutputStream;
35  import java.io.Closeable;
36  import java.io.File;
37  import java.io.FileInputStream;
38  import java.io.IOException;
39  import java.io.InputStream;
40  import java.io.RandomAccessFile;
41  import java.net.URL;
42  import java.nio.ByteBuffer;
43  import java.nio.channels.FileChannel;
44  import java.nio.channels.FileLock;
45  import java.text.MessageFormat;
46  import java.util.List;
47  import java.util.ResourceBundle;
48  import java.util.logging.Level;
49  import java.util.zip.GZIPInputStream;
50  import java.util.zip.GZIPOutputStream;
51  import javax.xml.bind.JAXBElement;
52  import javax.xml.bind.JAXBException;
53  import javax.xml.bind.Marshaller;
54  import javax.xml.bind.Unmarshaller;
55  import javax.xml.bind.util.JAXBResult;
56  import javax.xml.bind.util.JAXBSource;
57  import javax.xml.transform.Transformer;
58  import javax.xml.transform.TransformerException;
59  import javax.xml.validation.Schema;
60  import org.apache.bcel.classfile.Attribute;
61  import org.apache.bcel.classfile.ClassParser;
62  import org.apache.bcel.classfile.Constant;
63  import org.apache.bcel.classfile.ConstantPool;
64  import org.apache.bcel.classfile.ConstantUtf8;
65  import org.apache.bcel.classfile.JavaClass;
66  import org.apache.bcel.classfile.Unknown;
67  import org.jomc.model.Dependencies;
68  import org.jomc.model.Dependency;
69  import org.jomc.model.Implementation;
70  import org.jomc.model.Implementations;
71  import org.jomc.model.Message;
72  import org.jomc.model.Messages;
73  import org.jomc.model.ModelObject;
74  import org.jomc.model.Module;
75  import org.jomc.model.ObjectFactory;
76  import org.jomc.model.Properties;
77  import org.jomc.model.Property;
78  import org.jomc.model.Specification;
79  import org.jomc.model.SpecificationReference;
80  import org.jomc.model.Specifications;
81  import org.jomc.modlet.ModelContext;
82  import org.jomc.modlet.ModelException;
83  import org.jomc.modlet.ModelValidationReport;
84  import org.jomc.util.ParseException;
85  import org.jomc.util.TokenMgrError;
86  import org.jomc.util.VersionParser;
87  
88  /**
89   * Processes class files.
90   *
91   * <p><b>Use Cases:</b><br/><ul>
92   * <li>{@link #commitModelObjects(org.jomc.modlet.ModelContext, java.io.File) }</li>
93   * <li>{@link #commitModelObjects(org.jomc.model.Module, org.jomc.modlet.ModelContext, java.io.File) }</li>
94   * <li>{@link #commitModelObjects(org.jomc.model.Specification, org.jomc.modlet.ModelContext, java.io.File) }</li>
95   * <li>{@link #commitModelObjects(org.jomc.model.Implementation, org.jomc.modlet.ModelContext, java.io.File) }</li>
96   * <li>{@link #validateModelObjects(org.jomc.modlet.ModelContext) }</li>
97   * <li>{@link #validateModelObjects(org.jomc.model.Module, org.jomc.modlet.ModelContext) }</li>
98   * <li>{@link #validateModelObjects(org.jomc.model.Specification, org.jomc.modlet.ModelContext) }</li>
99   * <li>{@link #validateModelObjects(org.jomc.model.Implementation, org.jomc.modlet.ModelContext) }</li>
100  * <li>{@link #validateModelObjects(org.jomc.modlet.ModelContext, java.io.File) }</li>
101  * <li>{@link #validateModelObjects(org.jomc.model.Module, org.jomc.modlet.ModelContext, java.io.File) }</li>
102  * <li>{@link #validateModelObjects(org.jomc.model.Specification, org.jomc.modlet.ModelContext, java.io.File) }</li>
103  * <li>{@link #validateModelObjects(org.jomc.model.Implementation, org.jomc.modlet.ModelContext, java.io.File) }</li>
104  * <li>{@link #transformModelObjects(org.jomc.modlet.ModelContext, java.io.File, java.util.List) }</li>
105  * <li>{@link #transformModelObjects(org.jomc.model.Module, org.jomc.modlet.ModelContext, java.io.File, java.util.List) }</li>
106  * <li>{@link #transformModelObjects(org.jomc.model.Specification, org.jomc.modlet.ModelContext, java.io.File, java.util.List) }</li>
107  * <li>{@link #transformModelObjects(org.jomc.model.Specification, org.jomc.modlet.ModelContext, java.io.File, java.util.List) }</li>
108  * </ul></p>
109  *
110  * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
111  * @version $JOMC: ClassFileProcessor.java 4613 2012-09-22 10:07:08Z schulte $
112  *
113  * @see #getModules()
114  */
115 public class ClassFileProcessor extends JomcTool
116 {
117 
118     /** Empty byte array. */
119     private static final byte[] NO_BYTES =
120     {
121     };
122 
123     /** Creates a new {@code ClassFileProcessor} instance. */
124     public ClassFileProcessor()
125     {
126         super();
127     }
128 
129     /**
130      * Creates a new {@code ClassFileProcessor} instance taking a {@code ClassFileProcessor} instance to initialize the
131      * instance with.
132      *
133      * @param tool The instance to initialize the new instance with.
134      *
135      * @throws NullPointerException if {@code tool} is {@code null}.
136      * @throws IOException if copying {@code tool} fails.
137      */
138     public ClassFileProcessor( final ClassFileProcessor tool ) throws IOException
139     {
140         super( tool );
141     }
142 
143     /**
144      * Commits model objects of the modules of the instance to class files.
145      *
146      * @param context The model context to use for committing the model objects.
147      * @param classesDirectory The directory holding the class files.
148      *
149      * @throws NullPointerException if {@code context} or {@code classesDirectory} is {@code null}.
150      * @throws IOException if committing model objects fails.
151      *
152      * @see #commitModelObjects(org.jomc.model.Module, org.jomc.modlet.ModelContext, java.io.File)
153      */
154     public final void commitModelObjects( final ModelContext context, final File classesDirectory ) throws IOException
155     {
156         if ( context == null )
157         {
158             throw new NullPointerException( "context" );
159         }
160         if ( classesDirectory == null )
161         {
162             throw new NullPointerException( "classesDirectory" );
163         }
164 
165         try
166         {
167             if ( this.getModules() != null )
168             {
169                 final Marshaller m = context.createMarshaller( this.getModel().getIdentifier() );
170                 m.setSchema( context.createSchema( this.getModel().getIdentifier() ) );
171 
172                 this.commitModelObjects( this.getModules().getSpecifications(), this.getModules().getImplementations(),
173                                          m, classesDirectory );
174 
175             }
176             else if ( this.isLoggable( Level.WARNING ) )
177             {
178                 this.log( Level.WARNING, getMessage( "modulesNotFound", this.getModel().getIdentifier() ), null );
179             }
180         }
181         catch ( final ModelException e )
182         {
183             // JDK: As of JDK 6, "new IOException( message, cause )".
184             throw (IOException) new IOException( getMessage( e ) ).initCause( e );
185         }
186     }
187 
188     /**
189      * Commits model objects of a given module of the modules of the instance to class files.
190      *
191      * @param module The module to process.
192      * @param context The model context to use for committing the model objects.
193      * @param classesDirectory The directory holding the class files.
194      *
195      * @throws NullPointerException if {@code module}, {@code context} or {@code classesDirectory} is {@code null}.
196      * @throws IOException if committing model objects fails.
197      *
198      * @see #commitModelObjects(org.jomc.model.Specification, org.jomc.modlet.ModelContext, java.io.File)
199      * @see #commitModelObjects(org.jomc.model.Implementation, org.jomc.modlet.ModelContext, java.io.File)
200      */
201     public final void commitModelObjects( final Module module, final ModelContext context, final File classesDirectory )
202         throws IOException
203     {
204         if ( module == null )
205         {
206             throw new NullPointerException( "module" );
207         }
208         if ( context == null )
209         {
210             throw new NullPointerException( "context" );
211         }
212         if ( classesDirectory == null )
213         {
214             throw new NullPointerException( "classesDirectory" );
215         }
216 
217         try
218         {
219             if ( this.getModules() != null && this.getModules().getModule( module.getName() ) != null )
220             {
221                 final Marshaller m = context.createMarshaller( this.getModel().getIdentifier() );
222                 m.setSchema( context.createSchema( this.getModel().getIdentifier() ) );
223 
224                 this.commitModelObjects( module.getSpecifications(), module.getImplementations(), m, classesDirectory );
225             }
226             else if ( this.isLoggable( Level.WARNING ) )
227             {
228                 this.log( Level.WARNING, getMessage( "moduleNotFound", module.getName() ), null );
229             }
230         }
231         catch ( final ModelException e )
232         {
233             // JDK: As of JDK 6, "new IOException( message, cause )".
234             throw (IOException) new IOException( getMessage( e ) ).initCause( e );
235         }
236     }
237 
238     /**
239      * Commits model objects of a given specification of the modules of the instance to class files.
240      *
241      * @param specification The specification to process.
242      * @param context The model context to use for committing the model objects.
243      * @param classesDirectory The directory holding the class files.
244      *
245      * @throws NullPointerException if {@code specification}, {@code context} or {@code classesDirectory} is
246      * {@code null}.
247      * @throws IOException if committing model objects fails.
248      *
249      * @see #commitModelObjects(org.jomc.model.Specification, javax.xml.bind.Marshaller, org.apache.bcel.classfile.JavaClass)
250      */
251     public final void commitModelObjects( final Specification specification, final ModelContext context,
252                                           final File classesDirectory ) throws IOException
253     {
254         if ( specification == null )
255         {
256             throw new NullPointerException( "specification" );
257         }
258         if ( context == null )
259         {
260             throw new NullPointerException( "context" );
261         }
262         if ( classesDirectory == null )
263         {
264             throw new NullPointerException( "classesDirectory" );
265         }
266 
267         try
268         {
269             if ( this.getModules() != null
270                  && this.getModules().getSpecification( specification.getIdentifier() ) != null )
271             {
272                 final Marshaller m = context.createMarshaller( this.getModel().getIdentifier() );
273                 m.setSchema( context.createSchema( this.getModel().getIdentifier() ) );
274 
275                 this.commitModelObjects( specification, m, classesDirectory );
276             }
277             else if ( this.isLoggable( Level.WARNING ) )
278             {
279                 this.log( Level.WARNING, getMessage( "specificationNotFound", specification.getIdentifier() ), null );
280             }
281         }
282         catch ( final ModelException e )
283         {
284             // JDK: As of JDK 6, "new IOException( message, cause )".
285             throw (IOException) new IOException( getMessage( e ) ).initCause( e );
286         }
287     }
288 
289     /**
290      * Commits model objects of a given implementation of the modules of the instance to class files.
291      *
292      * @param implementation The implementation to process.
293      * @param context The model context to use for committing the model objects.
294      * @param classesDirectory The directory holding the class files.
295      *
296      * @throws NullPointerException if {@code implementation}, {@code context} or {@code classesDirectory} is
297      * {@code null}.
298      * @throws IOException if committing model objects fails.
299      *
300      * @see #commitModelObjects(org.jomc.model.Implementation, javax.xml.bind.Marshaller, org.apache.bcel.classfile.JavaClass)
301      */
302     public final void commitModelObjects( final Implementation implementation, final ModelContext context,
303                                           final File classesDirectory ) throws IOException
304     {
305         if ( implementation == null )
306         {
307             throw new NullPointerException( "implementation" );
308         }
309         if ( context == null )
310         {
311             throw new NullPointerException( "context" );
312         }
313         if ( classesDirectory == null )
314         {
315             throw new NullPointerException( "classesDirectory" );
316         }
317 
318         try
319         {
320             if ( this.getModules() != null
321                  && this.getModules().getImplementation( implementation.getIdentifier() ) != null )
322             {
323                 final Marshaller m = context.createMarshaller( this.getModel().getIdentifier() );
324                 m.setSchema( context.createSchema( this.getModel().getIdentifier() ) );
325 
326                 this.commitModelObjects( implementation, m, classesDirectory );
327             }
328             else if ( this.isLoggable( Level.WARNING ) )
329             {
330                 this.log( Level.WARNING, getMessage( "implementationNotFound", implementation.getIdentifier() ), null );
331             }
332         }
333         catch ( final ModelException e )
334         {
335             // JDK: As of JDK 6, "new IOException( message, cause )".
336             throw (IOException) new IOException( getMessage( e ) ).initCause( e );
337         }
338     }
339 
340     /**
341      * Commits model objects of a given specification of the modules of the instance to a given class file.
342      *
343      * @param specification The specification to process.
344      * @param marshaller The marshaller to use for committing the model objects.
345      * @param javaClass The java class to commit to.
346      *
347      * @throws NullPointerException if {@code specification}, {@code marshaller} or {@code javaClass} is {@code null}.
348      * @throws IOException if committing model objects fails.
349      */
350     public void commitModelObjects( final Specification specification, final Marshaller marshaller,
351                                     final JavaClass javaClass ) throws IOException
352     {
353         if ( specification == null )
354         {
355             throw new NullPointerException( "specification" );
356         }
357         if ( marshaller == null )
358         {
359             throw new NullPointerException( "marshaller" );
360         }
361         if ( javaClass == null )
362         {
363             throw new NullPointerException( "javaClass" );
364         }
365 
366         if ( this.getModules() != null
367              && this.getModules().getSpecification( specification.getIdentifier() ) != null )
368         {
369             this.setClassfileAttribute( javaClass, Specification.class.getName(), this.encodeModelObject(
370                 marshaller, new ObjectFactory().createSpecification( specification ) ) );
371 
372         }
373         else if ( this.isLoggable( Level.WARNING ) )
374         {
375             this.log( Level.WARNING, getMessage( "specificationNotFound", specification.getIdentifier() ), null );
376         }
377     }
378 
379     /**
380      * Commits model objects of a given implementation of the modules of the instance to a given class file.
381      *
382      * @param implementation The implementation to process.
383      * @param marshaller The marshaller to use for committing the model objects.
384      * @param javaClass The java class to commit to.
385      *
386      * @throws NullPointerException if {@code implementation}, {@code marshaller} or {@code javaClass} is {@code null}.
387      * @throws IOException if committing model objects fails.
388      */
389     public void commitModelObjects( final Implementation implementation, final Marshaller marshaller,
390                                     final JavaClass javaClass ) throws IOException
391     {
392         if ( implementation == null )
393         {
394             throw new NullPointerException( "implementation" );
395         }
396         if ( marshaller == null )
397         {
398             throw new NullPointerException( "marshaller" );
399         }
400         if ( javaClass == null )
401         {
402             throw new NullPointerException( "javaClass" );
403         }
404 
405         if ( this.getModules() != null
406              && this.getModules().getImplementation( implementation.getIdentifier() ) != null )
407         {
408             final ObjectFactory of = new ObjectFactory();
409 
410             Dependencies dependencies = this.getModules().getDependencies( implementation.getIdentifier() );
411             if ( dependencies == null )
412             {
413                 dependencies = new Dependencies();
414             }
415 
416             Properties properties = this.getModules().getProperties( implementation.getIdentifier() );
417             if ( properties == null )
418             {
419                 properties = new Properties();
420             }
421 
422             Messages messages = this.getModules().getMessages( implementation.getIdentifier() );
423             if ( messages == null )
424             {
425                 messages = new Messages();
426             }
427 
428             Specifications specifications = this.getModules().getSpecifications( implementation.getIdentifier() );
429             if ( specifications == null )
430             {
431                 specifications = new Specifications();
432             }
433 
434             for ( int i = 0, s0 = specifications.getReference().size(); i < s0; i++ )
435             {
436                 final SpecificationReference r = specifications.getReference().get( i );
437 
438                 if ( specifications.getSpecification( r.getIdentifier() ) == null && this.isLoggable( Level.WARNING ) )
439                 {
440                     this.log( Level.WARNING, getMessage( "unresolvedSpecification", r.getIdentifier(),
441                                                          implementation.getIdentifier() ), null );
442 
443                 }
444             }
445 
446             for ( int i = 0, s0 = dependencies.getDependency().size(); i < s0; i++ )
447             {
448                 final Dependency d = dependencies.getDependency().get( i );
449                 final Specification s = this.getModules().getSpecification( d.getIdentifier() );
450 
451                 if ( s != null )
452                 {
453                     if ( specifications.getSpecification( s.getIdentifier() ) == null )
454                     {
455                         specifications.getSpecification().add( s );
456                     }
457                 }
458                 else if ( this.isLoggable( Level.WARNING ) )
459                 {
460                     this.log( Level.WARNING, getMessage( "unresolvedDependencySpecification", d.getIdentifier(),
461                                                          d.getName(), implementation.getIdentifier() ), null );
462 
463                 }
464             }
465 
466             this.setClassfileAttribute( javaClass, Dependencies.class.getName(), this.encodeModelObject(
467                 marshaller, of.createDependencies( dependencies ) ) );
468 
469             this.setClassfileAttribute( javaClass, Properties.class.getName(), this.encodeModelObject(
470                 marshaller, of.createProperties( properties ) ) );
471 
472             this.setClassfileAttribute( javaClass, Messages.class.getName(), this.encodeModelObject(
473                 marshaller, of.createMessages( messages ) ) );
474 
475             this.setClassfileAttribute( javaClass, Specifications.class.getName(), this.encodeModelObject(
476                 marshaller, of.createSpecifications( specifications ) ) );
477 
478         }
479         else if ( this.isLoggable( Level.WARNING ) )
480         {
481             this.log( Level.WARNING, getMessage( "implementationNotFound", implementation.getIdentifier() ), null );
482         }
483     }
484 
485     /**
486      * Validates model objects of class files of the modules of the instance.
487      *
488      * @param context The model context to use for validating model objects.
489      *
490      * @return The report of the validation or {@code null}, if no model objects are found.
491      *
492      * @throws NullPointerException if {@code context} is {@code null}.
493      * @throws IOException if validating model objects fails.
494      *
495      * @see #validateModelObjects(org.jomc.model.Module, org.jomc.modlet.ModelContext)
496      */
497     public final ModelValidationReport validateModelObjects( final ModelContext context ) throws IOException
498     {
499         if ( context == null )
500         {
501             throw new NullPointerException( "context" );
502         }
503 
504         try
505         {
506             ModelValidationReport report = null;
507 
508             if ( this.getModules() != null )
509             {
510                 final Unmarshaller u = context.createUnmarshaller( this.getModel().getIdentifier() );
511                 u.setSchema( context.createSchema( this.getModel().getIdentifier() ) );
512                 report = this.validateModelObjects( this.getModules().getSpecifications(),
513                                                     this.getModules().getImplementations(), u, context );
514 
515             }
516             else if ( this.isLoggable( Level.WARNING ) )
517             {
518                 this.log( Level.WARNING, getMessage( "modulesNotFound", this.getModel().getIdentifier() ), null );
519             }
520 
521             return report;
522         }
523         catch ( final ModelException e )
524         {
525             // JDK: As of JDK 6, "new IOException( message, cause )".
526             throw (IOException) new IOException( getMessage( e ) ).initCause( e );
527         }
528     }
529 
530     /**
531      * Validates model objects of class files of a given module of the modules of the instance.
532      *
533      * @param module The module to process.
534      * @param context The model context to use for validating model objects.
535      *
536      * @return The report of the validation or {@code null}, if no model objects are found.
537      *
538      * @throws NullPointerException if {@code module} or {@code context} is {@code null}.
539      * @throws IOException if validating model objects fails.
540      *
541      * @see #validateModelObjects(org.jomc.model.Specification, org.jomc.modlet.ModelContext)
542      * @see #validateModelObjects(org.jomc.model.Implementation, org.jomc.modlet.ModelContext)
543      */
544     public final ModelValidationReport validateModelObjects( final Module module, final ModelContext context )
545         throws IOException
546     {
547         if ( module == null )
548         {
549             throw new NullPointerException( "module" );
550         }
551         if ( context == null )
552         {
553             throw new NullPointerException( "context" );
554         }
555 
556         try
557         {
558             ModelValidationReport report = null;
559 
560             if ( this.getModules() != null && this.getModules().getModule( module.getName() ) != null )
561             {
562                 final Unmarshaller u = context.createUnmarshaller( this.getModel().getIdentifier() );
563                 u.setSchema( context.createSchema( this.getModel().getIdentifier() ) );
564                 report = this.validateModelObjects( module.getSpecifications(), module.getImplementations(), u,
565                                                     context );
566 
567             }
568             else if ( this.isLoggable( Level.WARNING ) )
569             {
570                 this.log( Level.WARNING, getMessage( "moduleNotFound", module.getName() ), null );
571             }
572 
573             return report;
574         }
575         catch ( final ModelException e )
576         {
577             // JDK: As of JDK 6, "new IOException( message, cause )".
578             throw (IOException) new IOException( getMessage( e ) ).initCause( e );
579         }
580     }
581 
582     /**
583      * Validates model objects of class files of a given specification of the modules of the instance.
584      *
585      * @param specification The specification to process.
586      * @param context The model context to use for validating model objects.
587      *
588      * @return The report of the validation or {@code null}, if no model objects are found.
589      *
590      * @throws NullPointerException if {@code specification} or {@code context} is {@code null}.
591      *
592      * @throws IOException if validating model objects fails.
593      *
594      * @see #validateModelObjects(org.jomc.model.Specification, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass)
595      */
596     public final ModelValidationReport validateModelObjects( final Specification specification,
597                                                              final ModelContext context ) throws IOException
598     {
599         if ( specification == null )
600         {
601             throw new NullPointerException( "specification" );
602         }
603         if ( context == null )
604         {
605             throw new NullPointerException( "context" );
606         }
607 
608         try
609         {
610             ModelValidationReport report = null;
611 
612             if ( this.getModules() != null
613                  && this.getModules().getSpecification( specification.getIdentifier() ) != null )
614             {
615                 final Unmarshaller u = context.createUnmarshaller( this.getModel().getIdentifier() );
616                 u.setSchema( context.createSchema( this.getModel().getIdentifier() ) );
617                 report = this.validateModelObjects( specification, u, context );
618             }
619             else if ( this.isLoggable( Level.WARNING ) )
620             {
621                 this.log( Level.WARNING, getMessage( "specificationNotFound", specification.getIdentifier() ), null );
622             }
623 
624             return report;
625         }
626         catch ( final ModelException e )
627         {
628             // JDK: As of JDK 6, "new IOException( message, cause )".
629             throw (IOException) new IOException( getMessage( e ) ).initCause( e );
630         }
631     }
632 
633     /**
634      * Validates model objects of class files of a given implementation of the modules of the instance.
635      *
636      * @param implementation The implementation to process.
637      * @param context The model context to use for validating model objects.
638      *
639      * @return The report of the validation or {@code null}, if no model objects are found.
640      *
641      * @throws NullPointerException if {@code implementation} or {@code context} is {@code null}.
642      *
643      * @throws IOException if validating model objects fails.
644      *
645      * @see #validateModelObjects(org.jomc.model.Implementation, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass)
646      */
647     public final ModelValidationReport validateModelObjects( final Implementation implementation,
648                                                              final ModelContext context ) throws IOException
649     {
650         if ( implementation == null )
651         {
652             throw new NullPointerException( "implementation" );
653         }
654         if ( context == null )
655         {
656             throw new NullPointerException( "context" );
657         }
658 
659         try
660         {
661             ModelValidationReport report = null;
662 
663             if ( this.getModules() != null
664                  && this.getModules().getImplementation( implementation.getIdentifier() ) != null )
665             {
666                 final Unmarshaller u = context.createUnmarshaller( this.getModel().getIdentifier() );
667                 u.setSchema( context.createSchema( this.getModel().getIdentifier() ) );
668                 report = this.validateModelObjects( implementation, u, context );
669             }
670             else if ( this.isLoggable( Level.WARNING ) )
671             {
672                 this.log( Level.WARNING, getMessage( "implementationNotFound", implementation.getIdentifier() ), null );
673             }
674 
675             return report;
676         }
677         catch ( final ModelException e )
678         {
679             // JDK: As of JDK 6, "new IOException( message, cause )".
680             throw (IOException) new IOException( getMessage( e ) ).initCause( e );
681         }
682     }
683 
684     /**
685      * Validates model objects of class files of the modules of the instance.
686      *
687      * @param context The model context to use for validating model objects.
688      * @param classesDirectory The directory holding the class files.
689      *
690      * @return The report of the validation or {@code null}, if no model objects are found.
691      *
692      * @throws NullPointerException if {@code context} or {@code classesDirectory} is {@code null}.
693      * @throws IOException if validating model objects fails.
694      *
695      * @see #validateModelObjects(org.jomc.model.Module, org.jomc.modlet.ModelContext, java.io.File)
696      */
697     public final ModelValidationReport validateModelObjects( final ModelContext context, final File classesDirectory )
698         throws IOException
699     {
700         if ( context == null )
701         {
702             throw new NullPointerException( "context" );
703         }
704         if ( classesDirectory == null )
705         {
706             throw new NullPointerException( "classesDirectory" );
707         }
708 
709         try
710         {
711             ModelValidationReport report = null;
712 
713             if ( this.getModules() != null )
714             {
715                 final Unmarshaller u = context.createUnmarshaller( this.getModel().getIdentifier() );
716                 u.setSchema( context.createSchema( this.getModel().getIdentifier() ) );
717                 report = this.validateModelObjects( this.getModules().getSpecifications(),
718                                                     this.getModules().getImplementations(), u, classesDirectory );
719 
720             }
721             else if ( this.isLoggable( Level.WARNING ) )
722             {
723                 this.log( Level.WARNING, getMessage( "modulesNotFound", this.getModel().getIdentifier() ), null );
724             }
725 
726             return report;
727         }
728         catch ( final ModelException e )
729         {
730             // JDK: As of JDK 6, "new IOException( message, cause )".
731             throw (IOException) new IOException( getMessage( e ) ).initCause( e );
732         }
733     }
734 
735     /**
736      * Validates model objects of class files of a given module of the modules of the instance.
737      *
738      * @param module The module to process.
739      * @param context The model context to use for validating model objects.
740      * @param classesDirectory The directory holding the class files.
741      *
742      * @return The report of the validation or {@code null}, if no model objects are found.
743      *
744      * @throws NullPointerException if {@code module}, {@code context} or {@code classesDirectory} is {@code null}.
745      * @throws IOException if validating model objects fails.
746      *
747      * @see #validateModelObjects(org.jomc.model.Specification, org.jomc.modlet.ModelContext, java.io.File)
748      * @see #validateModelObjects(org.jomc.model.Implementation, org.jomc.modlet.ModelContext, java.io.File)
749      */
750     public final ModelValidationReport validateModelObjects( final Module module, final ModelContext context,
751                                                              final File classesDirectory ) throws IOException
752     {
753         if ( module == null )
754         {
755             throw new NullPointerException( "module" );
756         }
757         if ( context == null )
758         {
759             throw new NullPointerException( "context" );
760         }
761         if ( classesDirectory == null )
762         {
763             throw new NullPointerException( "classesDirectory" );
764         }
765 
766         try
767         {
768             ModelValidationReport report = null;
769 
770             if ( this.getModules() != null && this.getModules().getModule( module.getName() ) != null )
771             {
772                 final Unmarshaller u = context.createUnmarshaller( this.getModel().getIdentifier() );
773                 u.setSchema( context.createSchema( this.getModel().getIdentifier() ) );
774                 report = this.validateModelObjects( module.getSpecifications(), module.getImplementations(), u,
775                                                     classesDirectory );
776 
777             }
778             else if ( this.isLoggable( Level.WARNING ) )
779             {
780                 this.log( Level.WARNING, getMessage( "moduleNotFound", module.getName() ), null );
781             }
782 
783             return report;
784         }
785         catch ( final ModelException e )
786         {
787             // JDK: As of JDK 6, "new IOException( message, cause )".
788             throw (IOException) new IOException( getMessage( e ) ).initCause( e );
789         }
790     }
791 
792     /**
793      * Validates model objects of class files of a given specification of the modules of the instance.
794      *
795      * @param specification The specification to process.
796      * @param context The model context to use for validating model objects.
797      * @param classesDirectory The directory holding the class files.
798      *
799      * @return The report of the validation or {@code null}, if no model objects are found.
800      *
801      * @throws NullPointerException if {@code specification}, {@code context} or {@code classesDirectory} is
802      * {@code null}.
803      *
804      * @throws IOException if validating model objects fails.
805      *
806      * @see #validateModelObjects(org.jomc.model.Specification, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass)
807      */
808     public final ModelValidationReport validateModelObjects( final Specification specification,
809                                                              final ModelContext context, final File classesDirectory )
810         throws IOException
811     {
812         if ( specification == null )
813         {
814             throw new NullPointerException( "specification" );
815         }
816         if ( context == null )
817         {
818             throw new NullPointerException( "context" );
819         }
820         if ( classesDirectory == null )
821         {
822             throw new NullPointerException( "classesDirectory" );
823         }
824 
825         try
826         {
827             ModelValidationReport report = null;
828 
829             if ( this.getModules() != null
830                  && this.getModules().getSpecification( specification.getIdentifier() ) != null )
831             {
832                 final Unmarshaller u = context.createUnmarshaller( this.getModel().getIdentifier() );
833                 u.setSchema( context.createSchema( this.getModel().getIdentifier() ) );
834                 report = this.validateModelObjects( specification, u, classesDirectory );
835             }
836             else if ( this.isLoggable( Level.WARNING ) )
837             {
838                 this.log( Level.WARNING, getMessage( "specificationNotFound", specification.getIdentifier() ), null );
839             }
840 
841             return report;
842         }
843         catch ( final ModelException e )
844         {
845             // JDK: As of JDK 6, "new IOException( message, cause )".
846             throw (IOException) new IOException( getMessage( e ) ).initCause( e );
847         }
848     }
849 
850     /**
851      * Validates model objects of class files of a given implementation of the modules of the instance.
852      *
853      * @param implementation The implementation to process.
854      * @param context The model context to use for validating model objects.
855      * @param classesDirectory The directory holding the class files.
856      *
857      * @return The report of the validation or {@code null}, if no model objects are found.
858      *
859      * @throws NullPointerException if {@code implementation}, {@code context} or {@code classesDirectory} is
860      * {@code null}.
861      *
862      * @throws IOException if validating model objects fails.
863      *
864      * @see #validateModelObjects(org.jomc.model.Implementation, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass)
865      */
866     public final ModelValidationReport validateModelObjects( final Implementation implementation,
867                                                              final ModelContext context, final File classesDirectory )
868         throws IOException
869     {
870         if ( implementation == null )
871         {
872             throw new NullPointerException( "implementation" );
873         }
874         if ( context == null )
875         {
876             throw new NullPointerException( "context" );
877         }
878         if ( classesDirectory == null )
879         {
880             throw new NullPointerException( "classesDirectory" );
881         }
882 
883         try
884         {
885             ModelValidationReport report = null;
886 
887             if ( this.getModules() != null
888                  && this.getModules().getImplementation( implementation.getIdentifier() ) != null )
889             {
890                 final Unmarshaller u = context.createUnmarshaller( this.getModel().getIdentifier() );
891                 u.setSchema( context.createSchema( this.getModel().getIdentifier() ) );
892                 report = this.validateModelObjects( implementation, u, classesDirectory );
893             }
894             else if ( this.isLoggable( Level.WARNING ) )
895             {
896                 this.log( Level.WARNING, getMessage( "implementationNotFound", implementation.getIdentifier() ), null );
897             }
898 
899             return report;
900         }
901         catch ( final ModelException e )
902         {
903             // JDK: As of JDK 6, "new IOException( message, cause )".
904             throw (IOException) new IOException( getMessage( e ) ).initCause( e );
905         }
906     }
907 
908     /**
909      * Validates model objects of a given specification of the modules of the instance.
910      *
911      * @param specification The specification to process.
912      * @param unmarshaller The unmarshaller to use for validating model objects.
913      * @param javaClass The java class to validate.
914      *
915      * @return The report of the validation or {@code null}, if no model objects are found.
916      *
917      * @throws NullPointerException if {@code specification}, {@code unmarshaller} or {@code javaClass} is {@code null}.
918      * @throws IOException if validating model objects fails.
919      */
920     public ModelValidationReport validateModelObjects( final Specification specification,
921                                                        final Unmarshaller unmarshaller, final JavaClass javaClass )
922         throws IOException
923     {
924         if ( specification == null )
925         {
926             throw new NullPointerException( "specification" );
927         }
928         if ( unmarshaller == null )
929         {
930             throw new NullPointerException( "unmarshaller" );
931         }
932         if ( javaClass == null )
933         {
934             throw new NullPointerException( "javaClass" );
935         }
936 
937         ModelValidationReport report = null;
938 
939         if ( this.getModules() != null && this.getModules().getSpecification( specification.getIdentifier() ) != null )
940         {
941             report = new ModelValidationReport();
942 
943             Specification decoded = null;
944             final byte[] bytes = this.getClassfileAttribute( javaClass, Specification.class.getName() );
945             if ( bytes != null )
946             {
947                 decoded = this.decodeModelObject( unmarshaller, bytes, Specification.class );
948             }
949 
950             if ( decoded != null )
951             {
952                 if ( decoded.getMultiplicity() != specification.getMultiplicity() )
953                 {
954                     report.getDetails().add( new ModelValidationReport.Detail(
955                         "CLASS_ILLEGAL_SPECIFICATION_MULTIPLICITY", Level.SEVERE, getMessage(
956                         "illegalMultiplicity", specification.getIdentifier(), specification.getMultiplicity().value(),
957                         decoded.getMultiplicity().value() ),
958                         new ObjectFactory().createSpecification( specification ) ) );
959 
960                 }
961 
962                 if ( decoded.getScope() == null
963                      ? specification.getScope() != null
964                      : !decoded.getScope().equals( specification.getScope() ) )
965                 {
966                     report.getDetails().add( new ModelValidationReport.Detail(
967                         "CLASS_ILLEGAL_SPECIFICATION_SCOPE", Level.SEVERE, getMessage(
968                         "illegalScope", specification.getIdentifier(),
969                         specification.getScope() == null ? "Multiton" : specification.getScope(),
970                         decoded.getScope() == null ? "Multiton" : decoded.getScope() ),
971                         new ObjectFactory().createSpecification( specification ) ) );
972 
973                 }
974 
975                 if ( decoded.getClazz() == null
976                      ? specification.getClazz() != null
977                      : !decoded.getClazz().equals( specification.getClazz() ) )
978                 {
979                     report.getDetails().add( new ModelValidationReport.Detail(
980                         "CLASS_ILLEGAL_SPECIFICATION_CLASS", Level.SEVERE, getMessage(
981                         "illegalSpecificationClass", decoded.getIdentifier(),
982                         specification.getClazz(), decoded.getClazz() ),
983                         new ObjectFactory().createSpecification( specification ) ) );
984 
985                 }
986             }
987             else if ( this.isLoggable( Level.WARNING ) )
988             {
989                 this.log( Level.WARNING, getMessage( "cannotValidateSpecification", specification.getIdentifier(),
990                                                      Specification.class.getName() ), null );
991 
992             }
993         }
994         else if ( this.isLoggable( Level.WARNING ) )
995         {
996             this.log( Level.WARNING, getMessage( "specificationNotFound", specification.getIdentifier() ), null );
997         }
998 
999         return report;
1000     }
1001 
1002     /**
1003      * Validates model objects of a given implementation of the modules of the instance.
1004      *
1005      * @param implementation The implementation to process.
1006      * @param unmarshaller The unmarshaller to use for validating model objects.
1007      * @param javaClass The java class to validate.
1008      *
1009      * @return The report of the validation or {@code null}, if no model objects are found.
1010      *
1011      * @throws NullPointerException if {@code implementation}, {@code unmarshaller} or {@code javaClass} is {@code null}.
1012      * @throws IOException if validating model objects fails.
1013      */
1014     public ModelValidationReport validateModelObjects( final Implementation implementation,
1015                                                        final Unmarshaller unmarshaller, final JavaClass javaClass )
1016         throws IOException
1017     {
1018         if ( implementation == null )
1019         {
1020             throw new NullPointerException( "implementation" );
1021         }
1022         if ( unmarshaller == null )
1023         {
1024             throw new NullPointerException( "unmarshaller" );
1025         }
1026         if ( javaClass == null )
1027         {
1028             throw new NullPointerException( "javaClass" );
1029         }
1030 
1031         try
1032         {
1033             ModelValidationReport report = null;
1034 
1035             if ( this.getModules() != null
1036                  && this.getModules().getImplementation( implementation.getIdentifier() ) != null )
1037             {
1038                 report = new ModelValidationReport();
1039                 Dependencies dependencies = this.getModules().getDependencies( implementation.getIdentifier() );
1040                 if ( dependencies == null )
1041                 {
1042                     dependencies = new Dependencies();
1043                 }
1044 
1045                 Properties properties = this.getModules().getProperties( implementation.getIdentifier() );
1046                 if ( properties == null )
1047                 {
1048                     properties = new Properties();
1049                 }
1050 
1051                 Messages messages = this.getModules().getMessages( implementation.getIdentifier() );
1052                 if ( messages == null )
1053                 {
1054                     messages = new Messages();
1055                 }
1056 
1057                 Specifications specifications = this.getModules().getSpecifications( implementation.getIdentifier() );
1058                 if ( specifications == null )
1059                 {
1060                     specifications = new Specifications();
1061                 }
1062 
1063                 Dependencies decodedDependencies = null;
1064                 byte[] bytes = this.getClassfileAttribute( javaClass, Dependencies.class.getName() );
1065                 if ( bytes != null )
1066                 {
1067                     decodedDependencies = this.decodeModelObject( unmarshaller, bytes, Dependencies.class );
1068                 }
1069 
1070                 Properties decodedProperties = null;
1071                 bytes = this.getClassfileAttribute( javaClass, Properties.class.getName() );
1072                 if ( bytes != null )
1073                 {
1074                     decodedProperties = this.decodeModelObject( unmarshaller, bytes, Properties.class );
1075                 }
1076 
1077                 Messages decodedMessages = null;
1078                 bytes = this.getClassfileAttribute( javaClass, Messages.class.getName() );
1079                 if ( bytes != null )
1080                 {
1081                     decodedMessages = this.decodeModelObject( unmarshaller, bytes, Messages.class );
1082                 }
1083 
1084                 Specifications decodedSpecifications = null;
1085                 bytes = this.getClassfileAttribute( javaClass, Specifications.class.getName() );
1086                 if ( bytes != null )
1087                 {
1088                     decodedSpecifications = this.decodeModelObject( unmarshaller, bytes, Specifications.class );
1089                 }
1090 
1091                 if ( decodedDependencies != null )
1092                 {
1093                     for ( int i = 0, s0 = decodedDependencies.getDependency().size(); i < s0; i++ )
1094                     {
1095                         final Dependency decodedDependency = decodedDependencies.getDependency().get( i );
1096                         final Dependency dependency = dependencies.getDependency( decodedDependency.getName() );
1097                         final Specification s = this.getModules().getSpecification( decodedDependency.getIdentifier() );
1098 
1099                         if ( dependency == null )
1100                         {
1101                             report.getDetails().add( new ModelValidationReport.Detail(
1102                                 "CLASS_MISSING_IMPLEMENTATION_DEPENDENCY", Level.SEVERE, getMessage(
1103                                 "missingDependency", implementation.getIdentifier(), decodedDependency.getName() ),
1104                                 new ObjectFactory().createImplementation( implementation ) ) );
1105 
1106                         }
1107                         else if ( decodedDependency.getImplementationName() != null
1108                                   && dependency.getImplementationName() == null )
1109                         {
1110                             report.getDetails().add( new ModelValidationReport.Detail(
1111                                 "CLASS_MISSING_DEPENDENCY_IMPLEMENTATION_NAME", Level.SEVERE, getMessage(
1112                                 "missingDependencyImplementationName", implementation.getIdentifier(),
1113                                 decodedDependency.getName() ),
1114                                 new ObjectFactory().createImplementation( implementation ) ) );
1115 
1116                         }
1117 
1118                         if ( s != null && s.getVersion() != null && decodedDependency.getVersion() != null
1119                              && VersionParser.compare( decodedDependency.getVersion(), s.getVersion() ) > 0 )
1120                         {
1121                             final Module moduleOfSpecification =
1122                                 this.getModules().getModuleOfSpecification( s.getIdentifier() );
1123 
1124                             final Module moduleOfImplementation =
1125                                 this.getModules().getModuleOfImplementation( implementation.getIdentifier() );
1126 
1127                             report.getDetails().add( new ModelValidationReport.Detail(
1128                                 "CLASS_INCOMPATIBLE_IMPLEMENTATION_DEPENDENCY", Level.SEVERE, getMessage(
1129                                 "incompatibleDependency", javaClass.getClassName(),
1130                                 moduleOfImplementation == null ? "<>" : moduleOfImplementation.getName(),
1131                                 s.getIdentifier(),
1132                                 moduleOfSpecification == null ? "<>" : moduleOfSpecification.getName(),
1133                                 decodedDependency.getVersion(), s.getVersion() ),
1134                                 new ObjectFactory().createImplementation( implementation ) ) );
1135 
1136                         }
1137                     }
1138                 }
1139                 else if ( this.isLoggable( Level.WARNING ) )
1140                 {
1141                     this.log( Level.WARNING, getMessage( "cannotValidateImplementation", implementation.getIdentifier(),
1142                                                          Dependencies.class.getName() ), null );
1143 
1144                 }
1145 
1146                 if ( decodedProperties != null )
1147                 {
1148                     for ( int i = 0, s0 = decodedProperties.getProperty().size(); i < s0; i++ )
1149                     {
1150                         final Property decodedProperty = decodedProperties.getProperty().get( i );
1151                         final Property property = properties.getProperty( decodedProperty.getName() );
1152 
1153                         if ( property == null )
1154                         {
1155                             report.getDetails().add( new ModelValidationReport.Detail(
1156                                 "CLASS_MISSING_IMPLEMENTATION_PROPERTY", Level.SEVERE, getMessage(
1157                                 "missingProperty", implementation.getIdentifier(), decodedProperty.getName() ),
1158                                 new ObjectFactory().createImplementation( implementation ) ) );
1159 
1160                         }
1161                         else if ( decodedProperty.getType() == null
1162                                   ? property.getType() != null
1163                                   : !decodedProperty.getType().equals( property.getType() ) )
1164                         {
1165                             report.getDetails().add( new ModelValidationReport.Detail(
1166                                 "CLASS_ILLEGAL_IMPLEMENTATION_PROPERTY", Level.SEVERE, getMessage(
1167                                 "illegalPropertyType", implementation.getIdentifier(), decodedProperty.getName(),
1168                                 property.getType() == null ? "<>" : property.getType(),
1169                                 decodedProperty.getType() == null ? "<>" : decodedProperty.getType() ),
1170                                 new ObjectFactory().createImplementation( implementation ) ) );
1171 
1172                         }
1173                     }
1174                 }
1175                 else if ( this.isLoggable( Level.WARNING ) )
1176                 {
1177                     this.log( Level.WARNING, getMessage( "cannotValidateImplementation", implementation.getIdentifier(),
1178                                                          Properties.class.getName() ), null );
1179 
1180                 }
1181 
1182                 if ( decodedMessages != null )
1183                 {
1184                     for ( int i = 0, s0 = decodedMessages.getMessage().size(); i < s0; i++ )
1185                     {
1186                         final Message decodedMessage = decodedMessages.getMessage().get( i );
1187                         final Message message = messages.getMessage( decodedMessage.getName() );
1188 
1189                         if ( message == null )
1190                         {
1191                             report.getDetails().add( new ModelValidationReport.Detail(
1192                                 "CLASS_MISSING_IMPLEMENTATION_MESSAGE", Level.SEVERE, getMessage(
1193                                 "missingMessage", implementation.getIdentifier(), decodedMessage.getName() ),
1194                                 new ObjectFactory().createImplementation( implementation ) ) );
1195 
1196                         }
1197                     }
1198                 }
1199                 else if ( this.isLoggable( Level.WARNING ) )
1200                 {
1201                     this.log( Level.WARNING, getMessage( "cannotValidateImplementation", implementation.getIdentifier(),
1202                                                          Messages.class.getName() ), null );
1203 
1204                 }
1205 
1206                 if ( decodedSpecifications != null )
1207                 {
1208                     for ( int i = 0, s0 = decodedSpecifications.getSpecification().size(); i < s0; i++ )
1209                     {
1210                         final Specification decodedSpecification = decodedSpecifications.getSpecification().get( i );
1211                         final Specification specification =
1212                             this.getModules().getSpecification( decodedSpecification.getIdentifier() );
1213 
1214                         if ( specification == null )
1215                         {
1216                             report.getDetails().add( new ModelValidationReport.Detail(
1217                                 "CLASS_MISSING_SPECIFICATION", Level.SEVERE, getMessage(
1218                                 "missingSpecification", implementation.getIdentifier(),
1219                                 decodedSpecification.getIdentifier() ),
1220                                 new ObjectFactory().createImplementation( implementation ) ) );
1221 
1222                         }
1223                         else
1224                         {
1225                             if ( decodedSpecification.getMultiplicity() != specification.getMultiplicity() )
1226                             {
1227                                 report.getDetails().add( new ModelValidationReport.Detail(
1228                                     "CLASS_ILLEGAL_SPECIFICATION_MULTIPLICITY", Level.SEVERE, getMessage(
1229                                     "illegalMultiplicity", specification.getIdentifier(),
1230                                     specification.getMultiplicity().value(),
1231                                     decodedSpecification.getMultiplicity().value() ),
1232                                     new ObjectFactory().createImplementation( implementation ) ) );
1233 
1234                             }
1235 
1236                             if ( decodedSpecification.getScope() == null
1237                                  ? specification.getScope() != null
1238                                  : !decodedSpecification.getScope().equals( specification.getScope() ) )
1239                             {
1240                                 report.getDetails().add( new ModelValidationReport.Detail(
1241                                     "CLASS_ILLEGAL_SPECIFICATION_SCOPE", Level.SEVERE, getMessage(
1242                                     "illegalScope", decodedSpecification.getIdentifier(),
1243                                     specification.getScope() == null ? "Multiton" : specification.getScope(),
1244                                     decodedSpecification.getScope() == null ? "Multiton"
1245                                     : decodedSpecification.getScope() ),
1246                                     new ObjectFactory().createImplementation( implementation ) ) );
1247 
1248                             }
1249 
1250                             if ( decodedSpecification.getClazz() == null
1251                                  ? specification.getClazz() != null
1252                                  : !decodedSpecification.getClazz().equals( specification.getClazz() ) )
1253                             {
1254                                 report.getDetails().add( new ModelValidationReport.Detail(
1255                                     "CLASS_ILLEGAL_SPECIFICATION_CLASS", Level.SEVERE, getMessage(
1256                                     "illegalSpecificationClass", decodedSpecification.getIdentifier(),
1257                                     specification.getClazz(), decodedSpecification.getClazz() ),
1258                                     new ObjectFactory().createImplementation( implementation ) ) );
1259 
1260                             }
1261                         }
1262                     }
1263 
1264                     for ( int i = 0, s0 = decodedSpecifications.getReference().size(); i < s0; i++ )
1265                     {
1266                         final SpecificationReference decodedReference = decodedSpecifications.getReference().get( i );
1267                         final Specification specification =
1268                             specifications.getSpecification( decodedReference.getIdentifier() );
1269 
1270                         if ( specification == null )
1271                         {
1272                             report.getDetails().add( new ModelValidationReport.Detail(
1273                                 "CLASS_MISSING_SPECIFICATION", Level.SEVERE, getMessage(
1274                                 "missingSpecification", implementation.getIdentifier(),
1275                                 decodedReference.getIdentifier() ),
1276                                 new ObjectFactory().createImplementation( implementation ) ) );
1277 
1278                         }
1279                         else if ( decodedReference.getVersion() != null && specification.getVersion() != null
1280                                   && VersionParser.compare( decodedReference.getVersion(),
1281                                                             specification.getVersion() ) != 0 )
1282                         {
1283                             final Module moduleOfSpecification =
1284                                 this.getModules().getModuleOfSpecification( decodedReference.getIdentifier() );
1285 
1286                             final Module moduleOfImplementation =
1287                                 this.getModules().getModuleOfImplementation( implementation.getIdentifier() );
1288 
1289                             report.getDetails().add( new ModelValidationReport.Detail(
1290                                 "CLASS_INCOMPATIBLE_IMPLEMENTATION", Level.SEVERE, getMessage(
1291                                 "incompatibleImplementation", javaClass.getClassName(),
1292                                 moduleOfImplementation == null ? "<>" : moduleOfImplementation.getName(),
1293                                 specification.getIdentifier(),
1294                                 moduleOfSpecification == null ? "<>" : moduleOfSpecification.getName(),
1295                                 decodedReference.getVersion(), specification.getVersion() ),
1296                                 new ObjectFactory().createImplementation( implementation ) ) );
1297 
1298                         }
1299                     }
1300                 }
1301                 else if ( this.isLoggable( Level.WARNING ) )
1302                 {
1303                     this.log( Level.WARNING, getMessage( "cannotValidateImplementation", implementation.getIdentifier(),
1304                                                          Specifications.class.getName() ), null );
1305 
1306                 }
1307             }
1308             else if ( this.isLoggable( Level.WARNING ) )
1309             {
1310                 this.log( Level.WARNING, getMessage( "implementationNotFound", implementation.getIdentifier() ), null );
1311             }
1312 
1313             return report;
1314         }
1315         catch ( final ParseException e )
1316         {
1317             // JDK: As of JDK 6, "new IOException( message, cause )".
1318             throw (IOException) new IOException( getMessage( e ) ).initCause( e );
1319         }
1320         catch ( final TokenMgrError e )
1321         {
1322             // JDK: As of JDK 6, "new IOException( message, cause )".
1323             throw (IOException) new IOException( getMessage( e ) ).initCause( e );
1324         }
1325     }
1326 
1327     /**
1328      * Transforms model objects of class files of the modules of the instance.
1329      *
1330      * @param context The model context to use for transforming model objects.
1331      * @param classesDirectory The directory holding the class files.
1332      * @param transformers The transformers to use for transforming model objects.
1333      *
1334      * @throws NullPointerException if {@code context}, {@code classesDirectory} or {@code transformers} is
1335      * {@code null}.
1336      * @throws IOException if transforming model objects fails.
1337      *
1338      * @see #transformModelObjects(org.jomc.model.Module, org.jomc.modlet.ModelContext, java.io.File, java.util.List)
1339      */
1340     public final void transformModelObjects( final ModelContext context, final File classesDirectory,
1341                                              final List<Transformer> transformers ) throws IOException
1342     {
1343         if ( context == null )
1344         {
1345             throw new NullPointerException( "context" );
1346         }
1347         if ( classesDirectory == null )
1348         {
1349             throw new NullPointerException( "classesDirectory" );
1350         }
1351         if ( transformers == null )
1352         {
1353             throw new NullPointerException( "transformers" );
1354         }
1355         if ( !classesDirectory.isDirectory() )
1356         {
1357             throw new IOException( getMessage( "directoryNotFound", classesDirectory.getAbsolutePath() ) );
1358         }
1359 
1360         try
1361         {
1362             if ( this.getModules() != null )
1363             {
1364                 final Unmarshaller u = context.createUnmarshaller( this.getModel().getIdentifier() );
1365                 final Marshaller m = context.createMarshaller( this.getModel().getIdentifier() );
1366                 final Schema s = context.createSchema( this.getModel().getIdentifier() );
1367                 u.setSchema( s );
1368                 m.setSchema( s );
1369 
1370                 this.transformModelObjects( this.getModules().getSpecifications(),
1371                                             this.getModules().getImplementations(),
1372                                             u, m, classesDirectory, transformers );
1373 
1374             }
1375             else if ( this.isLoggable( Level.WARNING ) )
1376             {
1377                 this.log( Level.WARNING, getMessage( "modulesNotFound", this.getModel().getIdentifier() ), null );
1378             }
1379         }
1380         catch ( final ModelException e )
1381         {
1382             // JDK: As of JDK 6, "new IOException( message, cause )".
1383             throw (IOException) new IOException( getMessage( e ) ).initCause( e );
1384         }
1385     }
1386 
1387     /**
1388      * Transforms model objects of class files of a given module of the modules of the instance.
1389      *
1390      * @param module The module to process.
1391      * @param context The model context to use for transforming model objects.
1392      * @param classesDirectory The directory holding the class files.
1393      * @param transformers The transformers to use for transforming the model objects.
1394      *
1395      * @throws NullPointerException if {@code module}, {@code context}, {@code classesDirectory} or {@code transformers}
1396      * is {@code null}.
1397      * @throws IOException if transforming model objects fails.
1398      *
1399      * @see #transformModelObjects(org.jomc.model.Specification, org.jomc.modlet.ModelContext, java.io.File, java.util.List)
1400      * @see #transformModelObjects(org.jomc.model.Implementation, org.jomc.modlet.ModelContext, java.io.File, java.util.List)
1401      */
1402     public final void transformModelObjects( final Module module, final ModelContext context,
1403                                              final File classesDirectory, final List<Transformer> transformers )
1404         throws IOException
1405     {
1406         if ( module == null )
1407         {
1408             throw new NullPointerException( "module" );
1409         }
1410         if ( context == null )
1411         {
1412             throw new NullPointerException( "context" );
1413         }
1414         if ( classesDirectory == null )
1415         {
1416             throw new NullPointerException( "classesDirectory" );
1417         }
1418         if ( transformers == null )
1419         {
1420             throw new NullPointerException( "transformers" );
1421         }
1422         if ( !classesDirectory.isDirectory() )
1423         {
1424             throw new IOException( getMessage( "directoryNotFound", classesDirectory.getAbsolutePath() ) );
1425         }
1426 
1427         try
1428         {
1429             if ( this.getModules() != null && this.getModules().getModule( module.getName() ) != null )
1430             {
1431                 final Unmarshaller u = context.createUnmarshaller( this.getModel().getIdentifier() );
1432                 final Marshaller m = context.createMarshaller( this.getModel().getIdentifier() );
1433                 final Schema s = context.createSchema( this.getModel().getIdentifier() );
1434                 u.setSchema( s );
1435                 m.setSchema( s );
1436 
1437                 this.transformModelObjects( module.getSpecifications(), module.getImplementations(), u, m,
1438                                             classesDirectory, transformers );
1439 
1440             }
1441             else if ( this.isLoggable( Level.WARNING ) )
1442             {
1443                 this.log( Level.WARNING, getMessage( "moduleNotFound", module.getName() ), null );
1444             }
1445         }
1446         catch ( final ModelException e )
1447         {
1448             // JDK: As of JDK 6, "new IOException( message, cause )".
1449             throw (IOException) new IOException( getMessage( e ) ).initCause( e );
1450         }
1451     }
1452 
1453     /**
1454      * Transforms model objects of class files of a given specification of the modules of the instance.
1455      *
1456      * @param specification The specification to process.
1457      * @param context The model context to use for transforming model objects.
1458      * @param classesDirectory The directory holding the class files.
1459      * @param transformers The transformers to use for transforming the model objects.
1460      *
1461      * @throws NullPointerException if {@code specification}, {@code context}, {@code classesDirectory} or
1462      * {@code transformers} is {@code null}.
1463      * @throws IOException if transforming model objects fails.
1464      *
1465      * @see #transformModelObjects(org.jomc.model.Specification, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass, java.util.List)
1466      */
1467     public final void transformModelObjects( final Specification specification, final ModelContext context,
1468                                              final File classesDirectory, final List<Transformer> transformers )
1469         throws IOException
1470     {
1471         if ( specification == null )
1472         {
1473             throw new NullPointerException( "specification" );
1474         }
1475         if ( context == null )
1476         {
1477             throw new NullPointerException( "context" );
1478         }
1479         if ( classesDirectory == null )
1480         {
1481             throw new NullPointerException( "classesDirectory" );
1482         }
1483         if ( transformers == null )
1484         {
1485             throw new NullPointerException( "transformers" );
1486         }
1487         if ( !classesDirectory.isDirectory() )
1488         {
1489             throw new IOException( getMessage( "directoryNotFound", classesDirectory.getAbsolutePath() ) );
1490         }
1491 
1492         try
1493         {
1494             if ( this.getModules() != null
1495                  && this.getModules().getSpecification( specification.getIdentifier() ) != null )
1496             {
1497                 final Unmarshaller u = context.createUnmarshaller( this.getModel().getIdentifier() );
1498                 final Marshaller m = context.createMarshaller( this.getModel().getIdentifier() );
1499                 final Schema s = context.createSchema( this.getModel().getIdentifier() );
1500                 u.setSchema( s );
1501                 m.setSchema( s );
1502 
1503                 this.transformModelObjects( specification, m, u, classesDirectory, transformers );
1504             }
1505             else if ( this.isLoggable( Level.WARNING ) )
1506             {
1507                 this.log( Level.WARNING, getMessage( "specificationNotFound", specification.getIdentifier() ), null );
1508             }
1509         }
1510         catch ( final ModelException e )
1511         {
1512             // JDK: As of JDK 6, "new IOException( message, cause )".
1513             throw (IOException) new IOException( getMessage( e ) ).initCause( e );
1514         }
1515     }
1516 
1517     /**
1518      * Transforms model objects of class files of a given implementation of the modules of the instance.
1519      *
1520      * @param implementation The implementation to process.
1521      * @param context The model context to use for transforming model objects.
1522      * @param classesDirectory The directory holding the class files.
1523      * @param transformers The transformers to use for transforming the model objects.
1524      *
1525      * @throws NullPointerException if {@code implementation}, {@code context}, {@code classesDirectory} or
1526      * {@code transformers} is {@code null}.
1527      * @throws IOException if transforming model objects fails.
1528      *
1529      * @see #transformModelObjects(org.jomc.model.Implementation, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass, java.util.List)
1530      */
1531     public final void transformModelObjects( final Implementation implementation, final ModelContext context,
1532                                              final File classesDirectory, final List<Transformer> transformers )
1533         throws IOException
1534     {
1535         if ( implementation == null )
1536         {
1537             throw new NullPointerException( "implementation" );
1538         }
1539         if ( context == null )
1540         {
1541             throw new NullPointerException( "context" );
1542         }
1543         if ( classesDirectory == null )
1544         {
1545             throw new NullPointerException( "classesDirectory" );
1546         }
1547         if ( transformers == null )
1548         {
1549             throw new NullPointerException( "transformers" );
1550         }
1551         if ( !classesDirectory.isDirectory() )
1552         {
1553             throw new IOException( getMessage( "directoryNotFound", classesDirectory.getAbsolutePath() ) );
1554         }
1555 
1556         try
1557         {
1558             if ( this.getModules() != null
1559                  && this.getModules().getImplementation( implementation.getIdentifier() ) != null )
1560             {
1561                 final Unmarshaller u = context.createUnmarshaller( this.getModel().getIdentifier() );
1562                 final Marshaller m = context.createMarshaller( this.getModel().getIdentifier() );
1563                 final Schema s = context.createSchema( this.getModel().getIdentifier() );
1564                 u.setSchema( s );
1565                 m.setSchema( s );
1566 
1567                 this.transformModelObjects( implementation, m, u, classesDirectory, transformers );
1568             }
1569             else if ( this.isLoggable( Level.WARNING ) )
1570             {
1571                 this.log( Level.WARNING, getMessage( "implementationNotFound", implementation.getIdentifier() ), null );
1572             }
1573         }
1574         catch ( final ModelException e )
1575         {
1576             // JDK: As of JDK 6, "new IOException( message, cause )".
1577             throw (IOException) new IOException( getMessage( e ) ).initCause( e );
1578         }
1579     }
1580 
1581     /**
1582      * Transforms model objects of a given specification of the modules of the instance.
1583      *
1584      * @param specification The specification to process.
1585      * @param marshaller The marshaller to use for transforming model objects.
1586      * @param unmarshaller The unmarshaller to use for transforming model objects.
1587      * @param javaClass The java class to transform model objects of.
1588      * @param transformers The transformers to use for transforming the model objects.
1589      *
1590      * @throws NullPointerException if {@code specification}, {@code marshaller}, {@code unmarshaller},
1591      * {@code javaClass} or {@code transformers} is {@code null}.
1592      * @throws IOException if transforming model objects fails.
1593      */
1594     public void transformModelObjects( final Specification specification, final Marshaller marshaller,
1595                                        final Unmarshaller unmarshaller, final JavaClass javaClass,
1596                                        final List<Transformer> transformers ) throws IOException
1597     {
1598         if ( specification == null )
1599         {
1600             throw new NullPointerException( "specification" );
1601         }
1602         if ( marshaller == null )
1603         {
1604             throw new NullPointerException( "marshaller" );
1605         }
1606         if ( unmarshaller == null )
1607         {
1608             throw new NullPointerException( "unmarshaller" );
1609         }
1610         if ( javaClass == null )
1611         {
1612             throw new NullPointerException( "javaClass" );
1613         }
1614         if ( transformers == null )
1615         {
1616             throw new NullPointerException( "transformers" );
1617         }
1618 
1619         try
1620         {
1621             if ( this.getModules() != null
1622                  && this.getModules().getSpecification( specification.getIdentifier() ) != null )
1623             {
1624                 Specification decodedSpecification = null;
1625                 final ObjectFactory objectFactory = new ObjectFactory();
1626                 final byte[] bytes = this.getClassfileAttribute( javaClass, Specification.class.getName() );
1627                 if ( bytes != null )
1628                 {
1629                     decodedSpecification = this.decodeModelObject( unmarshaller, bytes, Specification.class );
1630                 }
1631 
1632                 if ( decodedSpecification != null )
1633                 {
1634                     for ( int i = 0, l = transformers.size(); i < l; i++ )
1635                     {
1636                         final JAXBSource source =
1637                             new JAXBSource( marshaller, objectFactory.createSpecification( decodedSpecification ) );
1638 
1639                         final JAXBResult result = new JAXBResult( unmarshaller );
1640                         transformers.get( i ).transform( source, result );
1641 
1642                         if ( result.getResult() instanceof JAXBElement<?>
1643                              && ( (JAXBElement<?>) result.getResult() ).getValue() instanceof Specification )
1644                         {
1645                             decodedSpecification = (Specification) ( (JAXBElement<?>) result.getResult() ).getValue();
1646                         }
1647                         else
1648                         {
1649                             throw new IOException( getMessage(
1650                                 "illegalSpecificationTransformationResult", specification.getIdentifier() ) );
1651 
1652                         }
1653                     }
1654 
1655                     this.setClassfileAttribute( javaClass, Specification.class.getName(), this.encodeModelObject(
1656                         marshaller, objectFactory.createSpecification( decodedSpecification ) ) );
1657 
1658                 }
1659             }
1660             else if ( this.isLoggable( Level.WARNING ) )
1661             {
1662                 this.log( Level.WARNING, getMessage( "specificationNotFound", specification.getIdentifier() ), null );
1663             }
1664         }
1665         catch ( final JAXBException e )
1666         {
1667             String message = getMessage( e );
1668             if ( message == null && e.getLinkedException() != null )
1669             {
1670                 message = getMessage( e.getLinkedException() );
1671             }
1672 
1673             // JDK: As of JDK 6, "new IOException( message, cause )".
1674             throw (IOException) new IOException( message ).initCause( e );
1675         }
1676         catch ( final TransformerException e )
1677         {
1678             String message = getMessage( e );
1679             if ( message == null && e.getException() != null )
1680             {
1681                 message = getMessage( e.getException() );
1682             }
1683 
1684             // JDK: As of JDK 6, "new IOException( message, cause )".
1685             throw (IOException) new IOException( message ).initCause( e );
1686         }
1687     }
1688 
1689     /**
1690      * Transforms model objects of a given implementation of the modules of the instance.
1691      *
1692      * @param implementation The implementation to process.
1693      * @param marshaller The marshaller to use for transforming model objects.
1694      * @param unmarshaller The unmarshaller to use for transforming model objects.
1695      * @param javaClass The java class to transform model object of.
1696      * @param transformers The transformers to use for transforming the model objects.
1697      *
1698      * @throws NullPointerException if {@code implementation}, {@code marshaller}, {@code unmarshaller},
1699      * {@code javaClass} or {@code transformers} is {@code null}.
1700      * @throws IOException if transforming model objects fails.
1701      */
1702     public void transformModelObjects( final Implementation implementation, final Marshaller marshaller,
1703                                        final Unmarshaller unmarshaller, final JavaClass javaClass,
1704                                        final List<Transformer> transformers ) throws IOException
1705     {
1706         if ( implementation == null )
1707         {
1708             throw new NullPointerException( "implementation" );
1709         }
1710         if ( marshaller == null )
1711         {
1712             throw new NullPointerException( "marshaller" );
1713         }
1714         if ( unmarshaller == null )
1715         {
1716             throw new NullPointerException( "unmarshaller" );
1717         }
1718         if ( javaClass == null )
1719         {
1720             throw new NullPointerException( "javaClass" );
1721         }
1722         if ( transformers == null )
1723         {
1724             throw new NullPointerException( "transformers" );
1725         }
1726 
1727         try
1728         {
1729             if ( this.getModules() != null
1730                  && this.getModules().getImplementation( implementation.getIdentifier() ) != null )
1731             {
1732                 Dependencies decodedDependencies = null;
1733                 byte[] bytes = this.getClassfileAttribute( javaClass, Dependencies.class.getName() );
1734                 if ( bytes != null )
1735                 {
1736                     decodedDependencies = this.decodeModelObject( unmarshaller, bytes, Dependencies.class );
1737                 }
1738 
1739                 Messages decodedMessages = null;
1740                 bytes = this.getClassfileAttribute( javaClass, Messages.class.getName() );
1741                 if ( bytes != null )
1742                 {
1743                     decodedMessages = this.decodeModelObject( unmarshaller, bytes, Messages.class );
1744                 }
1745 
1746                 Properties decodedProperties = null;
1747                 bytes = this.getClassfileAttribute( javaClass, Properties.class.getName() );
1748                 if ( bytes != null )
1749                 {
1750                     decodedProperties = this.decodeModelObject( unmarshaller, bytes, Properties.class );
1751                 }
1752 
1753                 Specifications decodedSpecifications = null;
1754                 bytes = this.getClassfileAttribute( javaClass, Specifications.class.getName() );
1755                 if ( bytes != null )
1756                 {
1757                     decodedSpecifications = this.decodeModelObject( unmarshaller, bytes, Specifications.class );
1758                 }
1759 
1760                 final ObjectFactory of = new ObjectFactory();
1761                 for ( int i = 0, l = transformers.size(); i < l; i++ )
1762                 {
1763                     final Transformer transformer = transformers.get( i );
1764 
1765                     if ( decodedDependencies != null )
1766                     {
1767                         final JAXBSource source =
1768                             new JAXBSource( marshaller, of.createDependencies( decodedDependencies ) );
1769 
1770                         final JAXBResult result = new JAXBResult( unmarshaller );
1771                         transformer.transform( source, result );
1772 
1773                         if ( result.getResult() instanceof JAXBElement<?>
1774                              && ( (JAXBElement<?>) result.getResult() ).getValue() instanceof Dependencies )
1775                         {
1776                             decodedDependencies = (Dependencies) ( (JAXBElement<?>) result.getResult() ).getValue();
1777                         }
1778                         else
1779                         {
1780                             throw new IOException( getMessage(
1781                                 "illegalImplementationTransformationResult", implementation.getIdentifier() ) );
1782 
1783                         }
1784                     }
1785 
1786                     if ( decodedMessages != null )
1787                     {
1788                         final JAXBSource source = new JAXBSource( marshaller, of.createMessages( decodedMessages ) );
1789                         final JAXBResult result = new JAXBResult( unmarshaller );
1790                         transformer.transform( source, result );
1791 
1792                         if ( result.getResult() instanceof JAXBElement<?>
1793                              && ( (JAXBElement<?>) result.getResult() ).getValue() instanceof Messages )
1794                         {
1795                             decodedMessages = (Messages) ( (JAXBElement<?>) result.getResult() ).getValue();
1796                         }
1797                         else
1798                         {
1799                             throw new IOException( getMessage(
1800                                 "illegalImplementationTransformationResult", implementation.getIdentifier() ) );
1801 
1802                         }
1803                     }
1804 
1805                     if ( decodedProperties != null )
1806                     {
1807                         final JAXBSource source = new JAXBSource( marshaller, of.createProperties( decodedProperties ) );
1808                         final JAXBResult result = new JAXBResult( unmarshaller );
1809                         transformer.transform( source, result );
1810 
1811                         if ( result.getResult() instanceof JAXBElement<?>
1812                              && ( (JAXBElement<?>) result.getResult() ).getValue() instanceof Properties )
1813                         {
1814                             decodedProperties = (Properties) ( (JAXBElement<?>) result.getResult() ).getValue();
1815                         }
1816                         else
1817                         {
1818                             throw new IOException( getMessage(
1819                                 "illegalImplementationTransformationResult", implementation.getIdentifier() ) );
1820 
1821                         }
1822                     }
1823 
1824                     if ( decodedSpecifications != null )
1825                     {
1826                         final JAXBSource source =
1827                             new JAXBSource( marshaller, of.createSpecifications( decodedSpecifications ) );
1828 
1829                         final JAXBResult result = new JAXBResult( unmarshaller );
1830                         transformer.transform( source, result );
1831 
1832                         if ( result.getResult() instanceof JAXBElement<?>
1833                              && ( (JAXBElement<?>) result.getResult() ).getValue() instanceof Specifications )
1834                         {
1835                             decodedSpecifications = (Specifications) ( (JAXBElement<?>) result.getResult() ).getValue();
1836                         }
1837                         else
1838                         {
1839                             throw new IOException( getMessage(
1840                                 "illegalImplementationTransformationResult", implementation.getIdentifier() ) );
1841 
1842                         }
1843                     }
1844                 }
1845 
1846                 if ( decodedDependencies != null )
1847                 {
1848                     this.setClassfileAttribute( javaClass, Dependencies.class.getName(), this.encodeModelObject(
1849                         marshaller, of.createDependencies( decodedDependencies ) ) );
1850 
1851                 }
1852 
1853                 if ( decodedMessages != null )
1854                 {
1855                     this.setClassfileAttribute( javaClass, Messages.class.getName(), this.encodeModelObject(
1856                         marshaller, of.createMessages( decodedMessages ) ) );
1857 
1858                 }
1859 
1860                 if ( decodedProperties != null )
1861                 {
1862                     this.setClassfileAttribute( javaClass, Properties.class.getName(), this.encodeModelObject(
1863                         marshaller, of.createProperties( decodedProperties ) ) );
1864 
1865                 }
1866 
1867                 if ( decodedSpecifications != null )
1868                 {
1869                     this.setClassfileAttribute( javaClass, Specifications.class.getName(), this.encodeModelObject(
1870                         marshaller, of.createSpecifications( decodedSpecifications ) ) );
1871 
1872                 }
1873             }
1874             else if ( this.isLoggable( Level.WARNING ) )
1875             {
1876                 this.log( Level.WARNING, getMessage( "implementationNotFound", implementation.getIdentifier() ), null );
1877             }
1878         }
1879         catch ( final JAXBException e )
1880         {
1881             String message = getMessage( e );
1882             if ( message == null && e.getLinkedException() != null )
1883             {
1884                 message = getMessage( e.getLinkedException() );
1885             }
1886 
1887             // JDK: As of JDK 6, "new IOException( message, cause )".
1888             throw (IOException) new IOException( message ).initCause( e );
1889         }
1890         catch ( final TransformerException e )
1891         {
1892             String message = getMessage( e );
1893             if ( message == null && e.getException() != null )
1894             {
1895                 message = getMessage( e.getException() );
1896             }
1897 
1898             // JDK: As of JDK 6, "new IOException( message, cause )".
1899             throw (IOException) new IOException( message ).initCause( e );
1900         }
1901     }
1902 
1903     /**
1904      * Gets an attribute from a java class.
1905      *
1906      * @param clazz The java class to get an attribute from.
1907      * @param attributeName The name of the attribute to get.
1908      *
1909      * @return The value of attribute {@code attributeName} of {@code clazz} or {@code null}, if no such attribute
1910      * exists.
1911      *
1912      * @throws NullPointerException if {@code clazz} or {@code attributeName} is {@code null}.
1913      * @throws IOException if getting the attribute fails.
1914      *
1915      * @see JavaClass#getAttributes()
1916      */
1917     public byte[] getClassfileAttribute( final JavaClass clazz, final String attributeName ) throws IOException
1918     {
1919         if ( clazz == null )
1920         {
1921             throw new NullPointerException( "clazz" );
1922         }
1923         if ( attributeName == null )
1924         {
1925             throw new NullPointerException( "attributeName" );
1926         }
1927 
1928         final Attribute[] attributes = clazz.getAttributes();
1929 
1930         for ( int i = attributes.length - 1; i >= 0; i-- )
1931         {
1932             final Constant constant = clazz.getConstantPool().getConstant( attributes[i].getNameIndex() );
1933 
1934             if ( constant instanceof ConstantUtf8 && attributeName.equals( ( (ConstantUtf8) constant ).getBytes() ) )
1935             {
1936                 final Unknown unknown = (Unknown) attributes[i];
1937                 return unknown.getBytes();
1938             }
1939         }
1940 
1941         return null;
1942     }
1943 
1944     /**
1945      * Adds or updates an attribute in a java class.
1946      *
1947      * @param clazz The class to update an attribute of.
1948      * @param attributeName The name of the attribute to update.
1949      * @param data The new data of the attribute to update the {@code clazz} with.
1950      *
1951      * @throws NullPointerException if {@code clazz} or {@code attributeName} is {@code null}.
1952      * @throws IOException if updating the class file fails.
1953      *
1954      * @see JavaClass#getAttributes()
1955      */
1956     public void setClassfileAttribute( final JavaClass clazz, final String attributeName, final byte[] data )
1957         throws IOException
1958     {
1959         if ( clazz == null )
1960         {
1961             throw new NullPointerException( "clazz" );
1962         }
1963         if ( attributeName == null )
1964         {
1965             throw new NullPointerException( "attributeName" );
1966         }
1967 
1968         final byte[] attributeData = data != null ? data : NO_BYTES;
1969 
1970         /*
1971          The JavaTM Virtual Machine Specification - Second Edition - Chapter 4.1
1972 
1973          A Java virtual machine implementation is required to silently ignore any
1974          or all attributes in the attributes table of a ClassFile structure that
1975          it does not recognize. Attributes not defined in this specification are
1976          not allowed to affect the semantics of the class file, but only to
1977          provide additional descriptive information (§4.7.1).
1978          */
1979         Attribute[] attributes = clazz.getAttributes();
1980 
1981         int attributeIndex = -1;
1982         int nameIndex = -1;
1983 
1984         for ( int i = attributes.length - 1; i >= 0; i-- )
1985         {
1986             final Constant constant = clazz.getConstantPool().getConstant( attributes[i].getNameIndex() );
1987 
1988             if ( constant instanceof ConstantUtf8 && attributeName.equals( ( (ConstantUtf8) constant ).getBytes() ) )
1989             {
1990                 attributeIndex = i;
1991                 nameIndex = attributes[i].getNameIndex();
1992             }
1993         }
1994 
1995         if ( nameIndex == -1 )
1996         {
1997             final Constant[] pool = clazz.getConstantPool().getConstantPool();
1998             final Constant[] tmp = new Constant[ pool.length + 1 ];
1999             System.arraycopy( pool, 0, tmp, 0, pool.length );
2000             tmp[pool.length] = new ConstantUtf8( attributeName );
2001             nameIndex = pool.length;
2002             clazz.setConstantPool( new ConstantPool( tmp ) );
2003         }
2004 
2005         final Unknown unknown = new Unknown( nameIndex, attributeData.length, attributeData, clazz.getConstantPool() );
2006 
2007         if ( attributeIndex == -1 )
2008         {
2009             final Attribute[] tmp = new Attribute[ attributes.length + 1 ];
2010             System.arraycopy( attributes, 0, tmp, 0, attributes.length );
2011             tmp[attributes.length] = unknown;
2012             attributes = tmp;
2013         }
2014         else
2015         {
2016             attributes[attributeIndex] = unknown;
2017         }
2018 
2019         clazz.setAttributes( attributes );
2020     }
2021 
2022     /**
2023      * Encodes a model object to a byte array.
2024      *
2025      * @param marshaller The marshaller to use for encoding the object.
2026      * @param modelObject The model object to encode.
2027      *
2028      * @return GZIP compressed XML document of {@code modelObject}.
2029      *
2030      * @throws NullPointerException if {@code marshaller} or {@code modelObject} is {@code null}.
2031      * @throws IOException if encoding {@code modelObject} fails.
2032      *
2033      * @see #decodeModelObject(javax.xml.bind.Unmarshaller, byte[], java.lang.Class)
2034      */
2035     public byte[] encodeModelObject( final Marshaller marshaller, final JAXBElement<? extends ModelObject> modelObject )
2036         throws IOException
2037     {
2038         if ( marshaller == null )
2039         {
2040             throw new NullPointerException( "marshaller" );
2041         }
2042         if ( modelObject == null )
2043         {
2044             throw new NullPointerException( "modelObject" );
2045         }
2046 
2047         try
2048         {
2049             final ByteArrayOutputStream baos = new ByteArrayOutputStream();
2050             final GZIPOutputStream out = new GZIPOutputStream( baos );
2051             marshaller.marshal( modelObject, out );
2052             out.close();
2053             return baos.toByteArray();
2054         }
2055         catch ( final JAXBException e )
2056         {
2057             String message = getMessage( e );
2058             if ( message == null && e.getLinkedException() != null )
2059             {
2060                 message = getMessage( e.getLinkedException() );
2061             }
2062 
2063             // JDK: As of JDK 6, "new IOException( message, cause )".
2064             throw (IOException) new IOException( message ).initCause( e );
2065         }
2066     }
2067 
2068     /**
2069      * Decodes a model object from a byte array.
2070      *
2071      * @param unmarshaller The unmarshaller to use for decoding the object.
2072      * @param bytes The encoded model object to decode.
2073      * @param type The class of the type of the encoded model object.
2074      * @param <T> The type of the encoded model object.
2075      *
2076      * @return Model object decoded from {@code bytes}.
2077      *
2078      * @throws NullPointerException if {@code unmarshaller}, {@code bytes} or {@code type} is {@code null}.
2079      * @throws IOException if decoding {@code bytes} fails.
2080      *
2081      * @see #encodeModelObject(javax.xml.bind.Marshaller, javax.xml.bind.JAXBElement)
2082      */
2083     public <T extends ModelObject> T decodeModelObject( final Unmarshaller unmarshaller, final byte[] bytes,
2084                                                         final Class<T> type ) throws IOException
2085     {
2086         if ( unmarshaller == null )
2087         {
2088             throw new NullPointerException( "unmarshaller" );
2089         }
2090         if ( bytes == null )
2091         {
2092             throw new NullPointerException( "bytes" );
2093         }
2094         if ( type == null )
2095         {
2096             throw new NullPointerException( "type" );
2097         }
2098 
2099         try
2100         {
2101             final ByteArrayInputStream bais = new ByteArrayInputStream( bytes );
2102             final GZIPInputStream in = new GZIPInputStream( bais );
2103             final JAXBElement<T> element = (JAXBElement<T>) unmarshaller.unmarshal( in );
2104             in.close();
2105             return element.getValue();
2106         }
2107         catch ( final JAXBException e )
2108         {
2109             String message = getMessage( e );
2110             if ( message == null && e.getLinkedException() != null )
2111             {
2112                 message = getMessage( e.getLinkedException() );
2113             }
2114 
2115             // JDK: As of JDK 6, "new IOException( message, cause )".
2116             throw (IOException) new IOException( message ).initCause( e );
2117         }
2118     }
2119 
2120     private void commitModelObjects( final Specifications specifications, final Implementations implementations,
2121                                      final Marshaller marshaller, final File classesDirectory ) throws IOException
2122     {
2123         if ( specifications != null )
2124         {
2125             for ( int i = specifications.getSpecification().size() - 1; i >= 0; i-- )
2126             {
2127                 this.commitModelObjects( specifications.getSpecification().get( i ), marshaller, classesDirectory );
2128             }
2129         }
2130 
2131         if ( implementations != null )
2132         {
2133             for ( int i = implementations.getImplementation().size() - 1; i >= 0; i-- )
2134             {
2135                 this.commitModelObjects( implementations.getImplementation().get( i ), marshaller, classesDirectory );
2136             }
2137         }
2138     }
2139 
2140     private void commitModelObjects( final Specification specification, final Marshaller marshaller,
2141                                      final File classesDirectory ) throws IOException
2142     {
2143         if ( specification.isClassDeclaration() )
2144         {
2145             final String classLocation = specification.getClazz().replace( '.', File.separatorChar ) + ".class";
2146             final File classFile = new File( classesDirectory, classLocation );
2147 
2148             if ( !classesDirectory.isDirectory() )
2149             {
2150                 throw new IOException( getMessage( "directoryNotFound", classesDirectory.getAbsolutePath() ) );
2151             }
2152             if ( !classFile.isFile() )
2153             {
2154                 throw new IOException( getMessage( "fileNotFound", classFile.getAbsolutePath() ) );
2155             }
2156             if ( !( classFile.canRead() && classFile.canWrite() ) )
2157             {
2158                 throw new IOException( getMessage( "fileAccessDenied", classFile.getAbsolutePath() ) );
2159             }
2160 
2161             if ( this.isLoggable( Level.INFO ) )
2162             {
2163                 this.log( Level.INFO, getMessage( "committing", classFile.getAbsolutePath() ), null );
2164             }
2165 
2166             final JavaClass javaClass = this.readJavaClass( classFile );
2167             this.commitModelObjects( specification, marshaller, javaClass );
2168             this.writeJavaClass( javaClass, classFile );
2169         }
2170     }
2171 
2172     private void commitModelObjects( final Implementation implementation, final Marshaller marshaller,
2173                                      final File classesDirectory ) throws IOException
2174     {
2175         if ( implementation.isClassDeclaration() )
2176         {
2177             final String classLocation = implementation.getClazz().replace( '.', File.separatorChar ) + ".class";
2178             final File classFile = new File( classesDirectory, classLocation );
2179 
2180             if ( !classesDirectory.isDirectory() )
2181             {
2182                 throw new IOException( getMessage( "directoryNotFound", classesDirectory.getAbsolutePath() ) );
2183             }
2184             if ( !classFile.isFile() )
2185             {
2186                 throw new IOException( getMessage( "fileNotFound", classFile.getAbsolutePath() ) );
2187             }
2188             if ( !( classFile.canRead() && classFile.canWrite() ) )
2189             {
2190                 throw new IOException( getMessage( "fileAccessDenied", classFile.getAbsolutePath() ) );
2191             }
2192 
2193             if ( this.isLoggable( Level.INFO ) )
2194             {
2195                 this.log( Level.INFO, getMessage( "committing", classFile.getAbsolutePath() ), null );
2196             }
2197 
2198             final JavaClass javaClass = this.readJavaClass( classFile );
2199             this.commitModelObjects( implementation, marshaller, javaClass );
2200             this.writeJavaClass( javaClass, classFile );
2201         }
2202     }
2203 
2204     private ModelValidationReport validateModelObjects( final Specifications specifications,
2205                                                         final Implementations implementations,
2206                                                         final Unmarshaller unmarshaller, final File classesDirectory )
2207         throws IOException
2208     {
2209         final ModelValidationReport report = new ModelValidationReport();
2210 
2211         if ( specifications != null )
2212         {
2213             for ( int i = 0, s0 = specifications.getSpecification().size(); i < s0; i++ )
2214             {
2215                 final ModelValidationReport current = this.validateModelObjects(
2216                     specifications.getSpecification().get( i ), unmarshaller, classesDirectory );
2217 
2218                 report.getDetails().addAll( current.getDetails() );
2219             }
2220         }
2221 
2222         if ( implementations != null )
2223         {
2224             for ( int i = 0, s0 = implementations.getImplementation().size(); i < s0; i++ )
2225             {
2226                 final ModelValidationReport current = this.validateModelObjects(
2227                     implementations.getImplementation().get( i ), unmarshaller, classesDirectory );
2228 
2229                 report.getDetails().addAll( current.getDetails() );
2230             }
2231         }
2232 
2233         return report;
2234     }
2235 
2236     private ModelValidationReport validateModelObjects( final Specification specification,
2237                                                         final Unmarshaller unmarshaller,
2238                                                         final File classesDirectory ) throws IOException
2239     {
2240         final ModelValidationReport report = new ModelValidationReport();
2241 
2242         if ( specification.isClassDeclaration() )
2243         {
2244             final String classLocation = specification.getClazz().replace( '.', File.separatorChar ) + ".class";
2245             final File classFile = new File( classesDirectory, classLocation );
2246 
2247             if ( !classesDirectory.isDirectory() )
2248             {
2249                 throw new IOException( getMessage( "directoryNotFound", classesDirectory.getAbsolutePath() ) );
2250             }
2251             if ( !classFile.isFile() )
2252             {
2253                 throw new IOException( getMessage( "fileNotFound", classFile.getAbsolutePath() ) );
2254             }
2255             if ( !classFile.canRead() )
2256             {
2257                 throw new IOException( getMessage( "fileAccessDenied", classFile.getAbsolutePath() ) );
2258             }
2259 
2260             if ( this.isLoggable( Level.INFO ) )
2261             {
2262                 this.log( Level.INFO, getMessage( "validating", classFile.getAbsolutePath() ), null );
2263             }
2264 
2265             final JavaClass javaClass = this.readJavaClass( classFile );
2266 
2267             report.getDetails().addAll(
2268                 this.validateModelObjects( specification, unmarshaller, javaClass ).getDetails() );
2269 
2270         }
2271 
2272         return report;
2273     }
2274 
2275     private ModelValidationReport validateModelObjects( final Implementation implementation,
2276                                                         final Unmarshaller unmarshaller,
2277                                                         final File classesDirectory ) throws IOException
2278     {
2279         final ModelValidationReport report = new ModelValidationReport();
2280 
2281         if ( implementation.isClassDeclaration() )
2282         {
2283             final String classLocation = implementation.getClazz().replace( '.', File.separatorChar ) + ".class";
2284             final File classFile = new File( classesDirectory, classLocation );
2285 
2286             if ( !classesDirectory.isDirectory() )
2287             {
2288                 throw new IOException( getMessage( "directoryNotFound", classesDirectory.getAbsolutePath() ) );
2289             }
2290             if ( !classFile.isFile() )
2291             {
2292                 throw new IOException( getMessage( "fileNotFound", classFile.getAbsolutePath() ) );
2293             }
2294             if ( !classFile.canRead() )
2295             {
2296                 throw new IOException( getMessage( "fileAccessDenied", classFile.getAbsolutePath() ) );
2297             }
2298 
2299             if ( this.isLoggable( Level.INFO ) )
2300             {
2301                 this.log( Level.INFO, getMessage( "validating", classFile.getAbsolutePath() ), null );
2302             }
2303 
2304             final JavaClass javaClass = this.readJavaClass( classFile );
2305 
2306             report.getDetails().addAll(
2307                 this.validateModelObjects( implementation, unmarshaller, javaClass ).getDetails() );
2308 
2309         }
2310 
2311         return report;
2312     }
2313 
2314     private ModelValidationReport validateModelObjects( final Specifications specifications,
2315                                                         final Implementations implementations,
2316                                                         final Unmarshaller unmarshaller, final ModelContext context )
2317         throws IOException, ModelException
2318     {
2319         final ModelValidationReport report = new ModelValidationReport();
2320 
2321         if ( specifications != null )
2322         {
2323             for ( int i = 0, s0 = specifications.getSpecification().size(); i < s0; i++ )
2324             {
2325                 final ModelValidationReport current = this.validateModelObjects(
2326                     specifications.getSpecification().get( i ), unmarshaller, context );
2327 
2328                 report.getDetails().addAll( current.getDetails() );
2329             }
2330         }
2331 
2332         if ( implementations != null )
2333         {
2334             for ( int i = 0, s0 = implementations.getImplementation().size(); i < s0; i++ )
2335             {
2336                 final ModelValidationReport current = this.validateModelObjects(
2337                     implementations.getImplementation().get( i ), unmarshaller, context );
2338 
2339                 report.getDetails().addAll( current.getDetails() );
2340             }
2341         }
2342 
2343         return report;
2344     }
2345 
2346     private ModelValidationReport validateModelObjects( final Specification specification,
2347                                                         final Unmarshaller unmarshaller,
2348                                                         final ModelContext context ) throws IOException, ModelException
2349     {
2350         final ModelValidationReport report = new ModelValidationReport();
2351 
2352         if ( specification.isClassDeclaration() )
2353         {
2354             final String classLocation = specification.getClazz().replace( '.', '/' ) + ".class";
2355 
2356             final URL classUrl = context.findResource( classLocation );
2357 
2358             if ( classUrl == null )
2359             {
2360                 throw new IOException( getMessage( "resourceNotFound", classLocation ) );
2361             }
2362 
2363             if ( this.isLoggable( Level.INFO ) )
2364             {
2365                 this.log( Level.INFO, getMessage( "validatingSpecification", specification.getIdentifier() ), null );
2366             }
2367 
2368             InputStream in = null;
2369             JavaClass javaClass = null;
2370             boolean suppressExceptionOnClose = true;
2371 
2372             try
2373             {
2374                 in = classUrl.openStream();
2375                 javaClass = new ClassParser( in, classUrl.toExternalForm() ).parse();
2376                 suppressExceptionOnClose = false;
2377             }
2378             finally
2379             {
2380                 try
2381                 {
2382                     if ( in != null )
2383                     {
2384                         in.close();
2385                     }
2386                 }
2387                 catch ( final IOException e )
2388                 {
2389                     if ( suppressExceptionOnClose )
2390                     {
2391                         this.log( Level.SEVERE, getMessage( e ), e );
2392                     }
2393                     else
2394                     {
2395                         throw e;
2396                     }
2397                 }
2398             }
2399 
2400             report.getDetails().addAll(
2401                 this.validateModelObjects( specification, unmarshaller, javaClass ).getDetails() );
2402 
2403         }
2404 
2405         return report;
2406     }
2407 
2408     private ModelValidationReport validateModelObjects( final Implementation implementation,
2409                                                         final Unmarshaller unmarshaller,
2410                                                         final ModelContext context ) throws IOException, ModelException
2411     {
2412         final ModelValidationReport report = new ModelValidationReport();
2413 
2414         if ( implementation.isClassDeclaration() )
2415         {
2416             final String classLocation = implementation.getClazz().replace( '.', '/' ) + ".class";
2417 
2418             final URL classUrl = context.findResource( classLocation );
2419 
2420             if ( classUrl == null )
2421             {
2422                 throw new IOException( getMessage( "resourceNotFound", classLocation ) );
2423             }
2424 
2425             if ( this.isLoggable( Level.INFO ) )
2426             {
2427                 this.log( Level.INFO, getMessage( "validatingImplementation", implementation.getIdentifier() ), null );
2428             }
2429 
2430             InputStream in = null;
2431             JavaClass javaClass = null;
2432             boolean suppressExceptionOnClose = true;
2433 
2434             try
2435             {
2436                 in = classUrl.openStream();
2437                 javaClass = new ClassParser( in, classUrl.toExternalForm() ).parse();
2438                 suppressExceptionOnClose = false;
2439             }
2440             finally
2441             {
2442                 try
2443                 {
2444                     if ( in != null )
2445                     {
2446                         in.close();
2447                     }
2448                 }
2449                 catch ( final IOException e )
2450                 {
2451                     if ( suppressExceptionOnClose )
2452                     {
2453                         this.log( Level.SEVERE, getMessage( e ), e );
2454                     }
2455                     else
2456                     {
2457                         throw e;
2458                     }
2459                 }
2460             }
2461 
2462             report.getDetails().addAll(
2463                 this.validateModelObjects( implementation, unmarshaller, javaClass ).getDetails() );
2464 
2465         }
2466 
2467         return report;
2468     }
2469 
2470     private void transformModelObjects( final Specifications specifications, final Implementations implementations,
2471                                         final Unmarshaller unmarshaller, final Marshaller marshaller,
2472                                         final File classesDirectory, final List<Transformer> transformers )
2473         throws IOException
2474     {
2475         if ( specifications != null )
2476         {
2477             for ( int i = 0, s0 = specifications.getSpecification().size(); i < s0; i++ )
2478             {
2479                 this.transformModelObjects( specifications.getSpecification().get( i ), marshaller, unmarshaller,
2480                                             classesDirectory, transformers );
2481 
2482             }
2483         }
2484 
2485         if ( implementations != null )
2486         {
2487             for ( int i = 0, s0 = implementations.getImplementation().size(); i < s0; i++ )
2488             {
2489                 this.transformModelObjects( implementations.getImplementation().get( i ), marshaller, unmarshaller,
2490                                             classesDirectory, transformers );
2491 
2492             }
2493         }
2494     }
2495 
2496     private void transformModelObjects( final Specification specification, final Marshaller marshaller,
2497                                         final Unmarshaller unmarshaller, final File classesDirectory,
2498                                         final List<Transformer> transformers ) throws IOException
2499     {
2500         if ( specification.isClassDeclaration() )
2501         {
2502             final String classLocation = specification.getClazz().replace( '.', File.separatorChar ) + ".class";
2503             final File classFile = new File( classesDirectory, classLocation );
2504 
2505             if ( !classesDirectory.isDirectory() )
2506             {
2507                 throw new IOException( getMessage( "directoryNotFound", classesDirectory.getAbsolutePath() ) );
2508             }
2509             if ( !classFile.isFile() )
2510             {
2511                 throw new IOException( getMessage( "fileNotFound", classFile.getAbsolutePath() ) );
2512             }
2513             if ( !( classFile.canRead() && classFile.canWrite() ) )
2514             {
2515                 throw new IOException( getMessage( "fileAccessDenied", classFile.getAbsolutePath() ) );
2516             }
2517 
2518             if ( this.isLoggable( Level.INFO ) )
2519             {
2520                 this.log( Level.INFO, getMessage( "transforming", classFile.getAbsolutePath() ), null );
2521             }
2522 
2523             final JavaClass javaClass = this.readJavaClass( classFile );
2524             this.transformModelObjects( specification, marshaller, unmarshaller, javaClass, transformers );
2525             this.writeJavaClass( javaClass, classFile );
2526         }
2527     }
2528 
2529     private void transformModelObjects( final Implementation implementation, final Marshaller marshaller,
2530                                         final Unmarshaller unmarshaller, final File classesDirectory,
2531                                         final List<Transformer> transformers ) throws IOException
2532     {
2533         if ( implementation.isClassDeclaration() )
2534         {
2535             final String classLocation = implementation.getClazz().replace( '.', File.separatorChar ) + ".class";
2536             final File classFile = new File( classesDirectory, classLocation );
2537 
2538             if ( !classesDirectory.isDirectory() )
2539             {
2540                 throw new IOException( getMessage( "directoryNotFound", classesDirectory.getAbsolutePath() ) );
2541             }
2542             if ( !classFile.isFile() )
2543             {
2544                 throw new IOException( getMessage( "fileNotFound", classFile.getAbsolutePath() ) );
2545             }
2546             if ( !( classFile.canRead() && classFile.canWrite() ) )
2547             {
2548                 throw new IOException( getMessage( "fileAccessDenied", classFile.getAbsolutePath() ) );
2549             }
2550 
2551             if ( this.isLoggable( Level.INFO ) )
2552             {
2553                 this.log( Level.INFO, getMessage( "transforming", classFile.getAbsolutePath() ), null );
2554             }
2555 
2556             final JavaClass javaClass = this.readJavaClass( classFile );
2557             this.transformModelObjects( implementation, marshaller, unmarshaller, javaClass, transformers );
2558             this.writeJavaClass( javaClass, classFile );
2559         }
2560     }
2561 
2562     private JavaClass readJavaClass( final File classFile ) throws IOException
2563     {
2564         FileInputStream in = null;
2565         FileChannel fileChannel = null;
2566         FileLock fileLock = null;
2567         boolean suppressExceptionOnClose = true;
2568 
2569         try
2570         {
2571             in = new FileInputStream( classFile );
2572             fileChannel = in.getChannel();
2573             fileLock = fileChannel.lock( 0, classFile.length(), true );
2574 
2575             final JavaClass javaClass = new ClassParser( in, classFile.getAbsolutePath() ).parse();
2576             suppressExceptionOnClose = false;
2577             return javaClass;
2578         }
2579         finally
2580         {
2581             this.releaseAndClose( fileLock, fileChannel, in, suppressExceptionOnClose );
2582         }
2583     }
2584 
2585     private void writeJavaClass( final JavaClass javaClass, final File classFile ) throws IOException
2586     {
2587         RandomAccessFile randomAccessFile = null;
2588         FileChannel fileChannel = null;
2589         FileLock fileLock = null;
2590         boolean suppressExceptionOnClose = true;
2591 
2592         final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
2593         javaClass.dump( byteStream );
2594         byteStream.close();
2595 
2596         final byte[] bytes = byteStream.toByteArray();
2597 
2598         try
2599         {
2600             randomAccessFile = new RandomAccessFile( classFile, "rw" );
2601             fileChannel = randomAccessFile.getChannel();
2602             fileLock = fileChannel.lock();
2603             fileChannel.truncate( bytes.length );
2604             fileChannel.position( 0L );
2605             fileChannel.write( ByteBuffer.wrap( bytes ) );
2606             fileChannel.force( true );
2607             suppressExceptionOnClose = false;
2608         }
2609         finally
2610         {
2611             this.releaseAndClose( fileLock, fileChannel, randomAccessFile, suppressExceptionOnClose );
2612         }
2613     }
2614 
2615     private void releaseAndClose( final FileLock fileLock, final FileChannel fileChannel,
2616                                   final Closeable closeable, final boolean suppressExceptions )
2617         throws IOException
2618     {
2619         try
2620         {
2621             if ( fileLock != null )
2622             {
2623                 fileLock.release();
2624             }
2625         }
2626         catch ( final IOException e )
2627         {
2628             if ( suppressExceptions )
2629             {
2630                 this.log( Level.SEVERE, null, e );
2631             }
2632             else
2633             {
2634                 throw e;
2635             }
2636         }
2637         finally
2638         {
2639             try
2640             {
2641                 if ( fileChannel != null )
2642                 {
2643                     fileChannel.close();
2644                 }
2645             }
2646             catch ( final IOException e )
2647             {
2648                 if ( suppressExceptions )
2649                 {
2650                     this.log( Level.SEVERE, null, e );
2651                 }
2652                 else
2653                 {
2654                     throw e;
2655                 }
2656             }
2657             finally
2658             {
2659                 try
2660                 {
2661                     if ( closeable != null )
2662                     {
2663                         closeable.close();
2664                     }
2665                 }
2666                 catch ( final IOException e )
2667                 {
2668                     if ( suppressExceptions )
2669                     {
2670                         this.log( Level.SEVERE, null, e );
2671                     }
2672                     else
2673                     {
2674                         throw e;
2675                     }
2676                 }
2677             }
2678         }
2679     }
2680 
2681     private static String getMessage( final String key, final Object... arguments )
2682     {
2683         return MessageFormat.format( ResourceBundle.getBundle(
2684             ClassFileProcessor.class.getName().replace( '.', '/' ) ).getString( key ), arguments );
2685 
2686     }
2687 
2688     private static String getMessage( final Throwable t )
2689     {
2690         return t != null ? t.getMessage() != null ? t.getMessage() : getMessage( t.getCause() ) : null;
2691     }
2692 
2693 }