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