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