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