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