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