View Javadoc

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