EMMA Coverage Report (generated Sun Jan 17 14:36:44 UTC 2010)
[all classes][org.jomc.tools]

COVERAGE SUMMARY FOR SOURCE FILE [JavaClasses.java]

nameclass, %method, %block, %line, %
JavaClasses.java100% (1/1)96%  (23/24)63%  (1896/2996)76%  (390.8/513)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class JavaClasses100% (1/1)96%  (23/24)63%  (1896/2996)76%  (390.8/513)
JavaClasses (JavaClasses): void 0%   (0/1)0%   (0/4)0%   (0/2)
validateClasses (Module, Unmarshaller, ClassLoader): ModelValidationReport 100% (1/1)13%  (21/163)20%  (6/30)
validateClasses (Unmarshaller, ClassLoader): ModelValidationReport 100% (1/1)31%  (14/45)40%  (4/10)
validateClasses (Implementation, Unmarshaller, JavaClass): ModelValidationReport 100% (1/1)33%  (300/898)56%  (53.9/96)
validateClasses (Specification, Unmarshaller, JavaClass): ModelValidationReport 100% (1/1)46%  (107/233)83%  (19.2/23)
commitClasses (Implementation, Marshaller, File): void 100% (1/1)66%  (180/271)73%  (32.8/45)
getJavaClass (File): JavaClass 100% (1/1)71%  (15/21)60%  (3/5)
encodeModelObject (Marshaller, JAXBElement): byte [] 100% (1/1)73%  (32/44)69%  (9/13)
getJavaClass (URL, String): JavaClass 100% (1/1)77%  (20/26)71%  (5/7)
decodeModelObject (Unmarshaller, byte [], Class): ModelObject 100% (1/1)78%  (42/54)73%  (11/15)
getJavaClass (InputStream, String): JavaClass 100% (1/1)82%  (27/33)80%  (8/10)
transformClasses (Specification, Marshaller, Unmarshaller, JavaClass, List): ... 100% (1/1)85%  (100/117)83%  (24/29)
transformClasses (Marshaller, Unmarshaller, File, List): void 100% (1/1)90%  (44/49)91%  (10/11)
getClassfileAttribute (JavaClass, String): byte [] 100% (1/1)91%  (48/53)91%  (10/11)
commitClasses (Specification, Marshaller, File): void 100% (1/1)93%  (83/89)89%  (16/18)
transformClasses (Module, Marshaller, Unmarshaller, File, List): void 100% (1/1)94%  (172/183)92%  (33/36)
transformClasses (Implementation, Marshaller, Unmarshaller, JavaClass, List):... 100% (1/1)94%  (267/284)92%  (58/63)
setClassfileAttribute (JavaClass, String, byte []): void 100% (1/1)96%  (128/133)97%  (28/29)
JavaClasses (): void 100% (1/1)100% (3/3)100% (2/2)
commitClasses (Marshaller, File): void 100% (1/1)100% (33/33)100% (7/7)
commitClasses (Module, Marshaller, File): void 100% (1/1)100% (64/64)100% (13/13)
getMessage (String, Object): String 100% (1/1)100% (18/18)100% (3/3)
validateClasses (Module, Unmarshaller, File): ModelValidationReport 100% (1/1)100% (133/133)100% (25/25)
validateClasses (Unmarshaller, File): ModelValidationReport 100% (1/1)100% (45/45)100% (10/10)

1/*
2 *   Copyright (c) 2009 The JOMC Project
3 *   Copyright (c) 2005 Christian Schulte <cs@jomc.org>
4 *   All rights reserved.
5 *
6 *   Redistribution and use in source and binary forms, with or without
7 *   modification, are permitted provided that the following conditions
8 *   are met:
9 *
10 *     o Redistributions of source code must retain the above copyright
11 *       notice, this list of conditions and the following disclaimer.
12 *
13 *     o Redistributions in binary form must reproduce the above copyright
14 *       notice, this list of conditions and the following disclaimer in
15 *       the documentation and/or other materials provided with the
16 *       distribution.
17 *
18 *   THIS SOFTWARE IS PROVIDED BY THE JOMC PROJECT AND CONTRIBUTORS "AS IS"
19 *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 *   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 *   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JOMC PROJECT OR
22 *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 *   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25 *   OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27 *   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28 *   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 *   $Id: JavaClasses.java 1237 2010-01-09 20:22:54Z schulte2005 $
31 *
32 */
33package org.jomc.tools;
34 
35import java.io.ByteArrayInputStream;
36import java.io.ByteArrayOutputStream;
37import java.io.File;
38import java.io.IOException;
39import java.io.InputStream;
40import java.net.URL;
41import java.text.MessageFormat;
42import java.util.List;
43import java.util.ResourceBundle;
44import java.util.logging.Level;
45import java.util.zip.GZIPInputStream;
46import java.util.zip.GZIPOutputStream;
47import javax.xml.bind.JAXBElement;
48import javax.xml.bind.JAXBException;
49import javax.xml.bind.Marshaller;
50import javax.xml.bind.Unmarshaller;
51import javax.xml.bind.util.JAXBResult;
52import javax.xml.bind.util.JAXBSource;
53import javax.xml.transform.Transformer;
54import javax.xml.transform.TransformerException;
55import org.apache.bcel.classfile.Attribute;
56import org.apache.bcel.classfile.ClassParser;
57import org.apache.bcel.classfile.Constant;
58import org.apache.bcel.classfile.ConstantPool;
59import org.apache.bcel.classfile.ConstantUtf8;
60import org.apache.bcel.classfile.JavaClass;
61import org.apache.bcel.classfile.Unknown;
62import org.jomc.model.Dependencies;
63import org.jomc.model.Dependency;
64import org.jomc.model.Implementation;
65import org.jomc.model.Message;
66import org.jomc.model.Messages;
67import org.jomc.model.ModelObject;
68import org.jomc.model.ModelValidationReport;
69import org.jomc.model.Module;
70import org.jomc.model.ObjectFactory;
71import org.jomc.model.Properties;
72import org.jomc.model.Property;
73import org.jomc.model.Specification;
74import org.jomc.model.SpecificationReference;
75import org.jomc.model.Specifications;
76import org.jomc.util.ParseException;
77import org.jomc.util.TokenMgrError;
78import org.jomc.util.VersionParser;
79 
80/**
81 * Manages Java classes.
82 *
83 * <p><b>Use cases</b><br/><ul>
84 * <li>{@link #commitClasses(javax.xml.bind.Marshaller, java.io.File) }</li>
85 * <li>{@link #commitClasses(org.jomc.model.Module, javax.xml.bind.Marshaller, java.io.File) }</li>
86 * <li>{@link #commitClasses(org.jomc.model.Specification, javax.xml.bind.Marshaller, java.io.File) }</li>
87 * <li>{@link #commitClasses(org.jomc.model.Implementation, javax.xml.bind.Marshaller, java.io.File) }</li>
88 * <li>{@link #validateClasses(javax.xml.bind.Unmarshaller, java.io.File) }</li>
89 * <li>{@link #validateClasses(javax.xml.bind.Unmarshaller, java.lang.ClassLoader) }</li>
90 * <li>{@link #validateClasses(org.jomc.model.Module, javax.xml.bind.Unmarshaller, java.io.File) }</li>
91 * <li>{@link #validateClasses(org.jomc.model.Module, javax.xml.bind.Unmarshaller, java.lang.ClassLoader) }</li>
92 * <li>{@link #validateClasses(org.jomc.model.Specification, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) }</li>
93 * <li>{@link #validateClasses(org.jomc.model.Implementation, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) }</li>
94 * <li>{@link #transformClasses(javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, java.io.File, java.util.List) }</li>
95 * <li>{@link #transformClasses(org.jomc.model.Module, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, java.io.File, java.util.List) }</li>
96 * <li>{@link #transformClasses(org.jomc.model.Specification, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass, java.util.List) }</li>
97 * <li>{@link #transformClasses(org.jomc.model.Implementation, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass, java.util.List) }</li>
98 * </ul></p>
99 *
100 * @author <a href="mailto:cs@jomc.org">Christian Schulte</a>
101 * @version $Id: JavaClasses.java 1237 2010-01-09 20:22:54Z schulte2005 $
102 *
103 * @see #getModules()
104 */
105public class JavaClasses extends JomcTool
106{
107 
108    /** Creates a new {@code JavaClasses} instance. */
109    public JavaClasses()
110    {
111        super();
112    }
113 
114    /**
115     * Creates a new {@code JavaClasses} instance taking a {@code JavaClasses} instance to initialize the instance with.
116     *
117     * @param tool The instance to initialize the new instance with,
118     */
119    public JavaClasses( final JavaClasses tool )
120    {
121        super( tool );
122    }
123 
124    /**
125     * Commits meta-data of the modules of the instance to compiled Java classes.
126     *
127     * @param marshaller The marshaller to use for committing the classes.
128     * @param classesDirectory The directory holding the compiled class files.
129     *
130     * @throws NullPointerException if {@code marshaller} or {@code classesDirectory} is {@code null}.
131     * @throws ToolException if committing meta-data fails.
132     *
133     * @see #commitClasses(org.jomc.model.Module, javax.xml.bind.Marshaller, java.io.File)
134     */
135    public void commitClasses( final Marshaller marshaller, final File classesDirectory ) throws ToolException
136    {
137        if ( marshaller == null )
138        {
139            throw new NullPointerException( "marshaller" );
140        }
141        if ( classesDirectory == null )
142        {
143            throw new NullPointerException( "classesDirectory" );
144        }
145 
146        for ( Module m : this.getModules().getModule() )
147        {
148            this.commitClasses( m, marshaller, classesDirectory );
149        }
150    }
151 
152    /**
153     * Commits meta-data of a given module of the modules of the instance to compiled Java classes.
154     *
155     * @param module The module to process.
156     * @param marshaller The marshaller to use for committing the classes.
157     * @param classesDirectory The directory holding the compiled class files.
158     *
159     * @throws NullPointerException if {@code module}, {@code marshaller} or {@code classesDirectory} is {@code null}.
160     * @throws ToolException if committing meta-data fails.
161     *
162     * @see #commitClasses(org.jomc.model.Specification, javax.xml.bind.Marshaller, java.io.File)
163     * @see #commitClasses(org.jomc.model.Implementation, javax.xml.bind.Marshaller, java.io.File)
164     */
165    public void commitClasses( final Module module, final Marshaller marshaller, final File classesDirectory )
166        throws ToolException
167    {
168        if ( module == null )
169        {
170            throw new NullPointerException( "module" );
171        }
172        if ( marshaller == null )
173        {
174            throw new NullPointerException( "marshaller" );
175        }
176        if ( classesDirectory == null )
177        {
178            throw new NullPointerException( "classesDirectory" );
179        }
180 
181        if ( module.getSpecifications() != null )
182        {
183            for ( Specification s : module.getSpecifications().getSpecification() )
184            {
185                this.commitClasses( s, marshaller, classesDirectory );
186            }
187        }
188        if ( module.getImplementations() != null )
189        {
190            for ( Implementation i : module.getImplementations().getImplementation() )
191            {
192                this.commitClasses( i, marshaller, classesDirectory );
193            }
194        }
195    }
196 
197    /**
198     * Commits meta-data of a given specification of the modules of the instance to compiled Java classes.
199     *
200     * @param specification The specification to process.
201     * @param marshaller The marshaller to use for committing the classes.
202     * @param classesDirectory The directory holding the compiled class files.
203     *
204     * @throws NullPointerException if {@code specification}, {@code marshaller} or {@code classesDirectory} is
205     * {@code null}.
206     * @throws ToolException if committing meta-data fails.
207     *
208     * @see org.jomc.model.ModelContext#createMarshaller()
209     */
210    public void commitClasses( final Specification specification, final Marshaller marshaller,
211                               final File classesDirectory ) throws ToolException
212    {
213        if ( specification == null )
214        {
215            throw new NullPointerException( "specification" );
216        }
217        if ( marshaller == null )
218        {
219            throw new NullPointerException( "marshaller" );
220        }
221        if ( classesDirectory == null )
222        {
223            throw new NullPointerException( "classesDirectory" );
224        }
225 
226        try
227        {
228            if ( specification.isClassDeclaration() )
229            {
230                final String classLocation = specification.getClazz().replace( '.', File.separatorChar ) + ".class";
231                final File classFile = new File( classesDirectory, classLocation );
232                if ( this.isLoggable( Level.INFO ) )
233                {
234                    this.log( Level.INFO, this.getMessage( "committing", new Object[]
235                        {
236                            classFile.getAbsolutePath()
237                        } ), null );
238 
239                }
240 
241                final JavaClass javaClass = this.getJavaClass( classFile );
242                this.setClassfileAttribute( javaClass, Specification.class.getName(), this.encodeModelObject(
243                    marshaller, new ObjectFactory().createSpecification( specification ) ) );
244 
245                javaClass.dump( classFile );
246            }
247        }
248        catch ( final IOException e )
249        {
250            throw new ToolException( e );
251        }
252    }
253 
254    /**
255     * Commits meta-data of a given implementation of the modules of the instance to compiled Java classes.
256     *
257     * @param implementation The implementation to process.
258     * @param marshaller The marshaller to use for committing the classes.
259     * @param classesDirectory The directory holding the compiled class files.
260     *
261     * @throws NullPointerException if {@code implementation}, {@code marshaller} or {@code classesDirectory} is
262     * {@code null}.
263     * @throws ToolException if committing meta-data fails.
264     *
265     * @see org.jomc.model.ModelContext#createMarshaller()
266     */
267    public void commitClasses( final Implementation implementation, final Marshaller marshaller,
268                               final File classesDirectory ) throws ToolException
269    {
270        if ( implementation == null )
271        {
272            throw new NullPointerException( "implementation" );
273        }
274        if ( marshaller == null )
275        {
276            throw new NullPointerException( "marshaller" );
277        }
278        if ( classesDirectory == null )
279        {
280            throw new NullPointerException( "classesDirectory" );
281        }
282 
283        try
284        {
285            if ( implementation.isClassDeclaration() )
286            {
287                Dependencies dependencies = this.getModules().getDependencies( implementation.getIdentifier() );
288                if ( dependencies == null )
289                {
290                    dependencies = new Dependencies();
291                }
292 
293                Properties properties = this.getModules().getProperties( implementation.getIdentifier() );
294                if ( properties == null )
295                {
296                    properties = new Properties();
297                }
298 
299                Messages messages = this.getModules().getMessages( implementation.getIdentifier() );
300                if ( messages == null )
301                {
302                    messages = new Messages();
303                }
304 
305                Specifications specifications = this.getModules().getSpecifications( implementation.getIdentifier() );
306                if ( specifications == null )
307                {
308                    specifications = new Specifications();
309                }
310 
311                for ( SpecificationReference r : specifications.getReference() )
312                {
313                    if ( specifications.getSpecification( r.getIdentifier() ) == null &&
314                         this.isLoggable( Level.WARNING ) )
315                    {
316                        this.log( Level.WARNING, this.getMessage( "unresolvedSpecification", new Object[]
317                            {
318                                r.getIdentifier(), implementation.getIdentifier()
319                            } ), null );
320 
321                    }
322                }
323 
324                for ( Dependency d : dependencies.getDependency() )
325                {
326                    final Specification s = this.getModules().getSpecification( d.getIdentifier() );
327 
328                    if ( s != null )
329                    {
330                        if ( specifications.getSpecification( s.getIdentifier() ) == null )
331                        {
332                            specifications.getSpecification().add( s );
333                        }
334                    }
335                    else if ( this.isLoggable( Level.WARNING ) )
336                    {
337                        this.log( Level.WARNING, this.getMessage( "unresolvedDependencySpecification", new Object[]
338                            {
339                                d.getIdentifier(), d.getName(), implementation.getIdentifier()
340                            } ), null );
341 
342                    }
343                }
344 
345                final String classLocation = implementation.getClazz().replace( '.', File.separatorChar ) + ".class";
346                final File classFile = new File( classesDirectory, classLocation );
347 
348                if ( this.isLoggable( Level.INFO ) )
349                {
350                    this.log( Level.INFO, this.getMessage( "committing", new Object[]
351                        {
352                            classFile.getAbsolutePath()
353                        } ), null );
354 
355                }
356 
357                final JavaClass javaClass = this.getJavaClass( classFile );
358                final ObjectFactory of = new ObjectFactory();
359 
360                this.setClassfileAttribute( javaClass, Dependencies.class.getName(), this.encodeModelObject(
361                    marshaller, of.createDependencies( dependencies ) ) );
362 
363                this.setClassfileAttribute( javaClass, Properties.class.getName(), this.encodeModelObject(
364                    marshaller, of.createProperties( properties ) ) );
365 
366                this.setClassfileAttribute( javaClass, Messages.class.getName(), this.encodeModelObject(
367                    marshaller, of.createMessages( messages ) ) );
368 
369                this.setClassfileAttribute( javaClass, Specifications.class.getName(), this.encodeModelObject(
370                    marshaller, of.createSpecifications( specifications ) ) );
371 
372                javaClass.dump( classFile );
373            }
374        }
375        catch ( final IOException e )
376        {
377            throw new ToolException( e );
378        }
379    }
380 
381    /**
382     * Validates compiled Java classes against the modules of the instance.
383     *
384     * @param unmarshaller The unmarshaller to use for validating classes.
385     * @param classesDirectory The directory holding the compiled class files.
386     *
387     * @return The report of the validation.
388     *
389     * @throws NullPointerException if {@code unmarshaller} or {@code classesDirectory} is {@code null}.
390     * @throws ToolException if reading class files fails.
391     *
392     * @see #validateClasses(org.jomc.model.Module, javax.xml.bind.Unmarshaller, java.io.File)
393     */
394    public ModelValidationReport validateClasses( final Unmarshaller unmarshaller, final File classesDirectory )
395        throws ToolException
396    {
397        if ( unmarshaller == null )
398        {
399            throw new NullPointerException( "unmarshaller" );
400        }
401        if ( classesDirectory == null )
402        {
403            throw new NullPointerException( "classesDirectory" );
404        }
405 
406        final ModelValidationReport report = new ModelValidationReport();
407 
408        for ( Module m : this.getModules().getModule() )
409        {
410            final ModelValidationReport current = this.validateClasses( m, unmarshaller, classesDirectory );
411            report.getDetails().addAll( current.getDetails() );
412        }
413 
414        return report;
415    }
416 
417    /**
418     * Validates compiled Java classes against the modules of the instance.
419     *
420     * @param unmarshaller The unmarshaller to use for validating classes.
421     * @param classLoader The class loader to search for classes.
422     *
423     * @return The report of the validation.
424     *
425     * @throws NullPointerException if {@code unmarshaller} or {@code classLoader} is {@code null}.
426     * @throws ToolException if reading class files fails.
427     *
428     * @see #validateClasses(org.jomc.model.Module, javax.xml.bind.Unmarshaller, java.lang.ClassLoader)
429     */
430    public ModelValidationReport validateClasses( final Unmarshaller unmarshaller, final ClassLoader classLoader )
431        throws ToolException
432    {
433        if ( unmarshaller == null )
434        {
435            throw new NullPointerException( "unmarshaller" );
436        }
437        if ( classLoader == null )
438        {
439            throw new NullPointerException( "classLoader" );
440        }
441 
442        final ModelValidationReport report = new ModelValidationReport();
443 
444        for ( Module m : this.getModules().getModule() )
445        {
446            final ModelValidationReport current = this.validateClasses( m, unmarshaller, classLoader );
447            report.getDetails().addAll( current.getDetails() );
448        }
449 
450        return report;
451    }
452 
453    /**
454     * Validates compiled Java classes against a given module of the modules of the instance.
455     *
456     * @param module The module to process.
457     * @param unmarshaller The unmarshaller to use for validating classes.
458     * @param classesDirectory The directory holding the compiled class files.
459     *
460     * @return The report of the validation.
461     *
462     * @throws NullPointerException if {@code module}, {@code unmarshaller} or {@code classesDirectory} is {@code null}.
463     * @throws ToolException if reading class files fails.
464     *
465     * @see #validateClasses(org.jomc.model.Specification, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass)
466     * @see #validateClasses(org.jomc.model.Implementation, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass)
467     */
468    public ModelValidationReport validateClasses( final Module module, final Unmarshaller unmarshaller,
469                                                  final File classesDirectory ) throws ToolException
470    {
471        if ( module == null )
472        {
473            throw new NullPointerException( "module" );
474        }
475        if ( unmarshaller == null )
476        {
477            throw new NullPointerException( "unmarshaller" );
478        }
479        if ( classesDirectory == null )
480        {
481            throw new NullPointerException( "classesDirectory" );
482        }
483 
484        final ModelValidationReport report = new ModelValidationReport();
485 
486        if ( module.getSpecifications() != null )
487        {
488            for ( Specification s : module.getSpecifications().getSpecification() )
489            {
490                if ( s.isClassDeclaration() )
491                {
492                    final String classLocation = s.getClazz().replace( '.', File.separatorChar ) + ".class";
493                    final File classFile = new File( classesDirectory, classLocation );
494                    final ModelValidationReport current =
495                        this.validateClasses( s, unmarshaller, this.getJavaClass( classFile ) );
496 
497                    report.getDetails().addAll( current.getDetails() );
498                }
499            }
500        }
501 
502        if ( module.getImplementations() != null )
503        {
504            for ( Implementation i : module.getImplementations().getImplementation() )
505            {
506                if ( i.isClassDeclaration() )
507                {
508                    final String classLocation = i.getClazz().replace( '.', File.separatorChar ) + ".class";
509                    final File classFile = new File( classesDirectory, classLocation );
510                    final JavaClass javaClass = this.getJavaClass( classFile );
511                    final ModelValidationReport current =
512                        this.validateClasses( i, unmarshaller, javaClass );
513 
514                    report.getDetails().addAll( current.getDetails() );
515                }
516            }
517        }
518 
519        return report;
520    }
521 
522    /**
523     * Validates compiled Java classes against a given module of the modules of the instance.
524     *
525     * @param module The module to process.
526     * @param unmarshaller The unmarshaller to use for validating classes.
527     * @param classLoader The class loader to search for classes.
528     *
529     * @return The report of the validation.
530     *
531     * @throws NullPointerException if {@code module}, {@code unmarshaller} or {@code classLoader} is {@code null}.
532     * @throws ToolException if reading class files fails.
533     *
534     * @see #validateClasses(org.jomc.model.Specification, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass)
535     * @see #validateClasses(org.jomc.model.Implementation, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass)
536     */
537    public ModelValidationReport validateClasses( final Module module, final Unmarshaller unmarshaller,
538                                                  final ClassLoader classLoader ) throws ToolException
539    {
540        if ( module == null )
541        {
542            throw new NullPointerException( "module" );
543        }
544        if ( unmarshaller == null )
545        {
546            throw new NullPointerException( "unmarshaller" );
547        }
548        if ( classLoader == null )
549        {
550            throw new NullPointerException( "classLoader" );
551        }
552 
553        final ModelValidationReport report = new ModelValidationReport();
554 
555        if ( module.getSpecifications() != null )
556        {
557            for ( Specification s : module.getSpecifications().getSpecification() )
558            {
559                if ( s.isClassDeclaration() )
560                {
561                    final String classLocation = s.getClazz().replace( '.', '/' ) + ".class";
562                    final URL classUrl = classLoader.getResource( classLocation );
563 
564                    if ( classUrl == null )
565                    {
566                        throw new ToolException( this.getMessage( "resourceNotFound", new Object[]
567                            {
568                                classLocation
569                            } ) );
570 
571                    }
572 
573                    final JavaClass javaClass = this.getJavaClass( classUrl, classLocation );
574                    final ModelValidationReport current =
575                        this.validateClasses( s, unmarshaller, javaClass );
576 
577                    report.getDetails().addAll( current.getDetails() );
578                }
579            }
580        }
581 
582        if ( module.getImplementations() != null )
583        {
584            for ( Implementation i : module.getImplementations().getImplementation() )
585            {
586                if ( i.isClassDeclaration() )
587                {
588                    final String classLocation = i.getClazz().replace( '.', '/' ) + ".class";
589                    final URL classUrl = classLoader.getResource( classLocation );
590 
591                    if ( classUrl == null )
592                    {
593                        throw new ToolException( this.getMessage( "resourceNotFound", new Object[]
594                            {
595                                classLocation
596                            } ) );
597 
598                    }
599 
600                    final JavaClass javaClass = this.getJavaClass( classUrl, classLocation );
601                    final ModelValidationReport current =
602                        this.validateClasses( i, unmarshaller, javaClass );
603 
604                    report.getDetails().addAll( current.getDetails() );
605                }
606            }
607        }
608 
609        return report;
610    }
611 
612    /**
613     * Validates compiled Java classes against a given specification of the modules of the instance.
614     *
615     * @param specification The specification to process.
616     * @param unmarshaller The unmarshaller to use for validating classes.
617     * @param javaClass The class to validate.
618     *
619     * @return The report of the validation.
620     *
621     * @throws NullPointerException if {@code specification}, {@code unmarshaller} or {@code javaClass} is {@code null}.
622     * @throws ToolException if reading class files fails.
623     *
624     * @see org.jomc.model.ModelContext#createUnmarshaller()
625     */
626    public ModelValidationReport validateClasses( final Specification specification,
627                                                  final Unmarshaller unmarshaller, final JavaClass javaClass )
628        throws ToolException
629    {
630        if ( specification == null )
631        {
632            throw new NullPointerException( "specification" );
633        }
634        if ( unmarshaller == null )
635        {
636            throw new NullPointerException( "unmarshaller" );
637        }
638        if ( javaClass == null )
639        {
640            throw new NullPointerException( "javaClass" );
641        }
642 
643        if ( this.isLoggable( Level.INFO ) )
644        {
645            this.log( Level.INFO, this.getMessage( "validatingSpecification", new Object[]
646                {
647                    specification.getIdentifier()
648                } ), null );
649 
650        }
651 
652        final ModelValidationReport report = new ModelValidationReport();
653 
654        Specification decoded = null;
655        final byte[] bytes = this.getClassfileAttribute( javaClass, Specification.class.getName() );
656        if ( bytes != null )
657        {
658            decoded = this.decodeModelObject( unmarshaller, bytes, Specification.class );
659        }
660 
661        if ( decoded != null )
662        {
663            if ( decoded.getMultiplicity() != specification.getMultiplicity() )
664            {
665                report.getDetails().add( new ModelValidationReport.Detail(
666                    "CLASS_ILLEGAL_SPECIFICATION_MULTIPLICITY", Level.SEVERE,
667                    this.getMessage( "illegalMultiplicity", new Object[]
668                    {
669                        specification.getIdentifier(), specification.getMultiplicity().value(),
670                        decoded.getMultiplicity().value()
671                    } ), new ObjectFactory().createSpecification( specification ) ) );
672 
673            }
674 
675            if ( decoded.getScope() == null
676                 ? specification.getScope() != null
677                 : !decoded.getScope().equals( specification.getScope() ) )
678            {
679                report.getDetails().add( new ModelValidationReport.Detail(
680                    "CLASS_ILLEGAL_SPECIFICATION_SCOPE", Level.SEVERE,
681                    this.getMessage( "illegalScope", new Object[]
682                    {
683                        specification.getIdentifier(),
684                        specification.getScope() == null ? "Multiton" : specification.getScope(),
685                        decoded.getScope() == null ? "Multiton" : decoded.getScope()
686                    } ), new ObjectFactory().createSpecification( specification ) ) );
687 
688            }
689 
690            if ( decoded.getClazz() == null
691                 ? specification.getClazz() != null
692                 : !decoded.getClazz().equals( specification.getClazz() ) )
693            {
694                report.getDetails().add( new ModelValidationReport.Detail(
695                    "CLASS_ILLEGAL_SPECIFICATION_CLASS", Level.SEVERE,
696                    this.getMessage( "illegalSpecificationClass", new Object[]
697                    {
698                        decoded.getIdentifier(), specification.getClazz(), decoded.getClazz()
699                    } ), new ObjectFactory().createSpecification( specification ) ) );
700 
701            }
702        }
703        else if ( this.isLoggable( Level.WARNING ) )
704        {
705            this.log( Level.WARNING, this.getMessage( "cannotValidateSpecification", new Object[]
706                {
707                    specification.getIdentifier(), Specification.class.getName()
708                } ), null );
709 
710        }
711 
712        return report;
713    }
714 
715    /**
716     * Validates compiled Java classes against a given implementation of the modules of the instance.
717     *
718     * @param implementation The implementation to process.
719     * @param unmarshaller The unmarshaller to use for validating classes.
720     * @param javaClass The class to validate.
721     *
722     * @return The report of the validation.
723     *
724     * @throws NullPointerException if {@code implementation}, {@code unmarshaller} or {@code javaClass} is
725     * {@code null}.
726     * @throws ToolException if reading class files fails.
727     *
728     * @see org.jomc.model.ModelContext#createUnmarshaller()
729     */
730    public ModelValidationReport validateClasses( final Implementation implementation,
731                                                  final Unmarshaller unmarshaller, final JavaClass javaClass )
732        throws ToolException
733    {
734        if ( implementation == null )
735        {
736            throw new NullPointerException( "implementation" );
737        }
738        if ( unmarshaller == null )
739        {
740            throw new NullPointerException( "unmarshaller" );
741        }
742        if ( javaClass == null )
743        {
744            throw new NullPointerException( "javaClass" );
745        }
746 
747        if ( this.isLoggable( Level.INFO ) )
748        {
749            this.log( Level.INFO, this.getMessage( "validatingImplementation", new Object[]
750                {
751                    implementation.getIdentifier()
752                } ), null );
753 
754        }
755 
756        final ModelValidationReport report = new ModelValidationReport();
757 
758        try
759        {
760            Dependencies dependencies = this.getModules().getDependencies( implementation.getIdentifier() );
761            if ( dependencies == null )
762            {
763                dependencies = new Dependencies();
764            }
765 
766            Properties properties = this.getModules().getProperties( implementation.getIdentifier() );
767            if ( properties == null )
768            {
769                properties = new Properties();
770            }
771 
772            Messages messages = this.getModules().getMessages( implementation.getIdentifier() );
773            if ( messages == null )
774            {
775                messages = new Messages();
776            }
777 
778            Specifications specifications = this.getModules().getSpecifications( implementation.getIdentifier() );
779            if ( specifications == null )
780            {
781                specifications = new Specifications();
782            }
783 
784            Dependencies decodedDependencies = null;
785            byte[] bytes = this.getClassfileAttribute( javaClass, Dependencies.class.getName() );
786            if ( bytes != null )
787            {
788                decodedDependencies = this.decodeModelObject( unmarshaller, bytes, Dependencies.class );
789            }
790 
791            Properties decodedProperties = null;
792            bytes = this.getClassfileAttribute( javaClass, Properties.class.getName() );
793            if ( bytes != null )
794            {
795                decodedProperties = this.decodeModelObject( unmarshaller, bytes, Properties.class );
796            }
797 
798            Messages decodedMessages = null;
799            bytes = this.getClassfileAttribute( javaClass, Messages.class.getName() );
800            if ( bytes != null )
801            {
802                decodedMessages = this.decodeModelObject( unmarshaller, bytes, Messages.class );
803            }
804 
805            Specifications decodedSpecifications = null;
806            bytes = this.getClassfileAttribute( javaClass, Specifications.class.getName() );
807            if ( bytes != null )
808            {
809                decodedSpecifications = this.decodeModelObject( unmarshaller, bytes, Specifications.class );
810            }
811 
812            if ( decodedDependencies != null )
813            {
814                for ( Dependency decodedDependency : decodedDependencies.getDependency() )
815                {
816                    final Dependency dependency = dependencies.getDependency( decodedDependency.getName() );
817 
818                    if ( dependency == null )
819                    {
820                        report.getDetails().add( new ModelValidationReport.Detail(
821                            "CLASS_MISSING_IMPLEMENTATION_DEPENDENCY", Level.SEVERE,
822                            this.getMessage( "missingDependency", new Object[]
823                            {
824                                implementation.getIdentifier(), decodedDependency.getName()
825                            } ), new ObjectFactory().createImplementation( implementation ) ) );
826 
827                    }
828 
829                    final Specification s = this.getModules().getSpecification( decodedDependency.getIdentifier() );
830 
831                    if ( s != null && s.getVersion() != null && decodedDependency.getVersion() != null &&
832                         VersionParser.compare( decodedDependency.getVersion(), s.getVersion() ) > 0 )
833                    {
834                        final Module moduleOfSpecification =
835                            this.getModules().getModuleOfSpecification( s.getIdentifier() );
836 
837                        final Module moduleOfImplementation =
838                            this.getModules().getModuleOfImplementation( implementation.getIdentifier() );
839 
840                        report.getDetails().add( new ModelValidationReport.Detail(
841                            "CLASS_INCOMPATIBLE_IMPLEMENTATION_DEPENDENCY", Level.SEVERE,
842                            this.getMessage( "incompatibleDependency", new Object[]
843                            {
844                                javaClass.getClassName(), moduleOfImplementation == null
845                                                          ? "<>" : moduleOfImplementation.getName(),
846                                s.getIdentifier(), moduleOfSpecification == null
847                                                   ? "<>" : moduleOfSpecification.getName(),
848                                decodedDependency.getVersion(), s.getVersion()
849                            } ), new ObjectFactory().createImplementation( implementation ) ) );
850 
851                    }
852                }
853            }
854            else if ( this.isLoggable( Level.WARNING ) )
855            {
856                this.log( Level.WARNING, this.getMessage( "cannotValidateImplementation", new Object[]
857                    {
858                        implementation.getIdentifier(), Dependencies.class.getName()
859                    } ), null );
860 
861            }
862 
863            if ( decodedProperties != null )
864            {
865                for ( Property decodedProperty : decodedProperties.getProperty() )
866                {
867                    final Property property = properties.getProperty( decodedProperty.getName() );
868 
869                    if ( property == null )
870                    {
871                        report.getDetails().add( new ModelValidationReport.Detail(
872                            "CLASS_MISSING_IMPLEMENTATION_PROPERTY", Level.SEVERE,
873                            this.getMessage( "missingProperty", new Object[]
874                            {
875                                implementation.getIdentifier(), decodedProperty.getName()
876                            } ), new ObjectFactory().createImplementation( implementation ) ) );
877 
878                    }
879                    else
880                    {
881                        if ( decodedProperty.getType() == null
882                             ? property.getType() != null
883                             : !decodedProperty.getType().equals( property.getType() ) )
884                        {
885                            report.getDetails().add( new ModelValidationReport.Detail(
886                                "CLASS_ILLEGAL_IMPLEMENTATION_PROPERTY", Level.SEVERE,
887                                this.getMessage( "illegalPropertyType", new Object[]
888                                {
889                                    implementation.getIdentifier(), decodedProperty.getName(),
890                                    property.getType() == null ? "default" : property.getType(),
891                                    decodedProperty.getType() == null ? "default" : decodedProperty.getType()
892                                } ), new ObjectFactory().createImplementation( implementation ) ) );
893 
894                        }
895                    }
896                }
897            }
898            else if ( this.isLoggable( Level.WARNING ) )
899            {
900                this.log( Level.WARNING, this.getMessage( "cannotValidateImplementation", new Object[]
901                    {
902                        implementation.getIdentifier(), Properties.class.getName()
903                    } ), null );
904 
905            }
906 
907            if ( decodedMessages != null )
908            {
909                for ( Message decodedMessage : decodedMessages.getMessage() )
910                {
911                    final Message message = messages.getMessage( decodedMessage.getName() );
912 
913                    if ( message == null )
914                    {
915                        report.getDetails().add( new ModelValidationReport.Detail(
916                            "CLASS_MISSING_IMPLEMENTATION_MESSAGE", Level.SEVERE,
917                            this.getMessage( "missingMessage", new Object[]
918                            {
919                                implementation.getIdentifier(), decodedMessage.getName()
920                            } ), new ObjectFactory().createImplementation( implementation ) ) );
921 
922                    }
923                }
924            }
925            else if ( this.isLoggable( Level.WARNING ) )
926            {
927                this.log( Level.WARNING, this.getMessage( "cannotValidateImplementation", new Object[]
928                    {
929                        implementation.getIdentifier(), Messages.class.getName()
930                    } ), null );
931 
932            }
933 
934            if ( decodedSpecifications != null )
935            {
936                for ( Specification decodedSpecification : decodedSpecifications.getSpecification() )
937                {
938                    final Specification specification =
939                        this.getModules().getSpecification( decodedSpecification.getIdentifier() );
940 
941                    if ( specification == null )
942                    {
943                        report.getDetails().add( new ModelValidationReport.Detail(
944                            "CLASS_MISSING_SPECIFICATION", Level.SEVERE,
945                            this.getMessage( "missingSpecification", new Object[]
946                            {
947                                implementation.getIdentifier(), decodedSpecification.getIdentifier()
948                            } ), new ObjectFactory().createImplementation( implementation ) ) );
949 
950                    }
951                    else
952                    {
953                        if ( decodedSpecification.getMultiplicity() != specification.getMultiplicity() )
954                        {
955                            report.getDetails().add( new ModelValidationReport.Detail(
956                                "CLASS_ILLEGAL_SPECIFICATION_MULTIPLICITY", Level.SEVERE,
957                                this.getMessage( "illegalMultiplicity", new Object[]
958                                {
959                                    specification.getIdentifier(), specification.getMultiplicity().value(),
960                                    decodedSpecification.getMultiplicity().value()
961                                } ), new ObjectFactory().createImplementation( implementation ) ) );
962 
963                        }
964 
965                        if ( decodedSpecification.getScope() == null
966                             ? specification.getScope() != null
967                             : !decodedSpecification.getScope().equals( specification.getScope() ) )
968                        {
969                            report.getDetails().add( new ModelValidationReport.Detail(
970                                "CLASS_ILLEGAL_SPECIFICATION_SCOPE", Level.SEVERE,
971                                this.getMessage( "illegalScope", new Object[]
972                                {
973                                    decodedSpecification.getIdentifier(),
974                                    specification.getScope() == null
975                                    ? "Multiton" : specification.getScope(),
976                                    decodedSpecification.getScope() == null
977                                    ? "Multiton" : decodedSpecification.getScope()
978                                } ), new ObjectFactory().createImplementation( implementation ) ) );
979 
980                        }
981 
982                        if ( decodedSpecification.getClazz() == null
983                             ? specification.getClazz() != null
984                             : !decodedSpecification.getClazz().equals( specification.getClazz() ) )
985                        {
986                            report.getDetails().add( new ModelValidationReport.Detail(
987                                "CLASS_ILLEGAL_SPECIFICATION_CLASS", Level.SEVERE,
988                                this.getMessage( "illegalSpecificationClass", new Object[]
989                                {
990                                    decodedSpecification.getIdentifier(), specification.getClazz(),
991                                    decodedSpecification.getClazz()
992                                } ), new ObjectFactory().createImplementation( implementation ) ) );
993 
994                        }
995                    }
996                }
997 
998                for ( SpecificationReference decodedReference : decodedSpecifications.getReference() )
999                {
1000                    final Specification specification =
1001                        specifications.getSpecification( decodedReference.getIdentifier() );
1002 
1003                    if ( specification == null )
1004                    {
1005                        report.getDetails().add( new ModelValidationReport.Detail(
1006                            "CLASS_MISSING_SPECIFICATION", Level.SEVERE,
1007                            this.getMessage( "missingSpecification", new Object[]
1008                            {
1009                                implementation.getIdentifier(), decodedReference.getIdentifier()
1010                            } ), new ObjectFactory().createImplementation( implementation ) ) );
1011 
1012                    }
1013                    else if ( decodedReference.getVersion() != null && specification.getVersion() != null &&
1014                              VersionParser.compare( decodedReference.getVersion(), specification.getVersion() ) != 0 )
1015                    {
1016                        final Module moduleOfSpecification =
1017                            this.getModules().getModuleOfSpecification( decodedReference.getIdentifier() );
1018 
1019                        final Module moduleOfImplementation =
1020                            this.getModules().getModuleOfImplementation( implementation.getIdentifier() );
1021 
1022                        report.getDetails().add( new ModelValidationReport.Detail(
1023                            "CLASS_INCOMPATIBLE_IMPLEMENTATION", Level.SEVERE,
1024                            this.getMessage( "incompatibleImplementation", new Object[]
1025                            {
1026                                javaClass.getClassName(), moduleOfImplementation == null
1027                                                          ? "<>" : moduleOfImplementation.getName(),
1028                                specification.getIdentifier(), moduleOfSpecification == null
1029                                                               ? "<>" : moduleOfSpecification.getName(),
1030                                decodedReference.getVersion(), specification.getVersion()
1031                            } ), new ObjectFactory().createImplementation( implementation ) ) );
1032 
1033                    }
1034                }
1035            }
1036            else if ( this.isLoggable( Level.WARNING ) )
1037            {
1038                this.log( Level.WARNING, this.getMessage( "cannotValidateImplementation", new Object[]
1039                    {
1040                        implementation.getIdentifier(), Specifications.class.getName()
1041                    } ), null );
1042 
1043            }
1044 
1045            return report;
1046        }
1047        catch ( final ParseException e )
1048        {
1049            throw new ToolException( e );
1050        }
1051        catch ( final TokenMgrError e )
1052        {
1053            throw new ToolException( e );
1054        }
1055    }
1056 
1057    /**
1058     * Transforms committed meta-data of compiled Java classes of the modules of the instance.
1059     *
1060     * @param marshaller The marshaller to use for transforming classes.
1061     * @param unmarshaller The unmarshaller to use for transforming classes.
1062     * @param classesDirectory The directory holding the compiled class files.
1063     * @param transformers The transformers to use for transforming the classes.
1064     *
1065     * @throws NullPointerException if {@code marshaller}, {@code unmarshaller}, {@code classesDirectory} or
1066     * {@code transformers} is {@code null}.
1067     * @throws ToolException if accessing class files fails.
1068     *
1069     * @see #transformClasses(org.jomc.model.Module, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, java.io.File, java.util.List)
1070     */
1071    public void transformClasses( final Marshaller marshaller, final Unmarshaller unmarshaller,
1072                                  final File classesDirectory, final List<Transformer> transformers )
1073        throws ToolException
1074    {
1075        if ( marshaller == null )
1076        {
1077            throw new NullPointerException( "marshaller" );
1078        }
1079        if ( unmarshaller == null )
1080        {
1081            throw new NullPointerException( "unmarshaller" );
1082        }
1083        if ( transformers == null )
1084        {
1085            throw new NullPointerException( "transformers" );
1086        }
1087        if ( classesDirectory == null )
1088        {
1089            throw new NullPointerException( "classesDirectory" );
1090        }
1091 
1092        for ( Module m : this.getModules().getModule() )
1093        {
1094            this.transformClasses( m, marshaller, unmarshaller, classesDirectory, transformers );
1095        }
1096    }
1097 
1098    /**
1099     * Transforms committed meta-data of compiled Java classes of a given module of the modules of the instance.
1100     *
1101     * @param module The module to process.
1102     * @param marshaller The marshaller to use for transforming classes.
1103     * @param unmarshaller The unmarshaller to use for transforming classes.
1104     * @param classesDirectory The directory holding the compiled class files.
1105     * @param transformers The transformers to use for transforming the classes.
1106     *
1107     * @throws NullPointerException if {@code module}, {@code marshaller}, {@code unmarshaller},
1108     * {@code classesDirectory} or {@code transformers} is {@code null}.
1109     * @throws ToolException if accessing class files fails.
1110     *
1111     * @see #transformClasses(org.jomc.model.Specification, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass, java.util.List)
1112     * @see #transformClasses(org.jomc.model.Implementation, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass, java.util.List)
1113     */
1114    public void transformClasses( final Module module, final Marshaller marshaller, final Unmarshaller unmarshaller,
1115                                  final File classesDirectory, final List<Transformer> transformers )
1116        throws ToolException
1117    {
1118        if ( module == null )
1119        {
1120            throw new NullPointerException( "module" );
1121        }
1122        if ( marshaller == null )
1123        {
1124            throw new NullPointerException( "marshaller" );
1125        }
1126        if ( unmarshaller == null )
1127        {
1128            throw new NullPointerException( "unmarshaller" );
1129        }
1130        if ( transformers == null )
1131        {
1132            throw new NullPointerException( "transformers" );
1133        }
1134        if ( classesDirectory == null )
1135        {
1136            throw new NullPointerException( "classesDirectory" );
1137        }
1138 
1139        try
1140        {
1141            if ( module.getSpecifications() != null )
1142            {
1143                for ( Specification s : module.getSpecifications().getSpecification() )
1144                {
1145                    if ( s.isClassDeclaration() )
1146                    {
1147                        final String classLocation = s.getIdentifier().replace( '.', File.separatorChar ) + ".class";
1148                        final File classFile = new File( classesDirectory, classLocation );
1149 
1150                        if ( this.isLoggable( Level.INFO ) )
1151                        {
1152                            this.log( Level.INFO, this.getMessage( "transforming", new Object[]
1153                                {
1154                                    classFile.getAbsolutePath()
1155                                } ), null );
1156 
1157                        }
1158 
1159                        final JavaClass javaClass = this.getJavaClass( classFile );
1160                        this.transformClasses( s, marshaller, unmarshaller, javaClass, transformers );
1161                        javaClass.dump( classFile );
1162                    }
1163                }
1164            }
1165 
1166            if ( module.getImplementations() != null )
1167            {
1168                for ( Implementation i : module.getImplementations().getImplementation() )
1169                {
1170                    if ( i.isClassDeclaration() )
1171                    {
1172                        final String classLocation = i.getClazz().replace( '.', File.separatorChar ) + ".class";
1173                        final File classFile = new File( classesDirectory, classLocation );
1174 
1175                        if ( this.isLoggable( Level.INFO ) )
1176                        {
1177                            this.log( Level.INFO, this.getMessage( "transforming", new Object[]
1178                                {
1179                                    classFile.getAbsolutePath()
1180                                } ), null );
1181 
1182                        }
1183 
1184                        final JavaClass javaClass = this.getJavaClass( classFile );
1185                        this.transformClasses( i, marshaller, unmarshaller, javaClass, transformers );
1186                        javaClass.dump( classFile );
1187                    }
1188                }
1189            }
1190        }
1191        catch ( final IOException e )
1192        {
1193            throw new ToolException( e );
1194        }
1195    }
1196 
1197    /**
1198     * Transforms committed meta-data of compiled Java classes of a given specification of the modules of the instance.
1199     *
1200     * @param specification The specification to process.
1201     * @param marshaller The marshaller to use for transforming classes.
1202     * @param unmarshaller The unmarshaller to use for transforming classes.
1203     * @param javaClass The java class to process.
1204     * @param transformers The transformers to use for transforming the classes.
1205     *
1206     * @throws NullPointerException if {@code specification}, {@code marshaller}, {@code unmarshaller},
1207     * {@code javaClass} or {@code transformers} is {@code null}.
1208     * @throws ToolException if accessing class files fails.
1209     *
1210     * @see org.jomc.model.ModelContext#createMarshaller()
1211     * @see org.jomc.model.ModelContext#createUnmarshaller()
1212     */
1213    public void transformClasses( final Specification specification, final Marshaller marshaller,
1214                                  final Unmarshaller unmarshaller, final JavaClass javaClass,
1215                                  final List<Transformer> transformers ) throws ToolException
1216    {
1217        if ( specification == null )
1218        {
1219            throw new NullPointerException( "specification" );
1220        }
1221        if ( marshaller == null )
1222        {
1223            throw new NullPointerException( "marshaller" );
1224        }
1225        if ( unmarshaller == null )
1226        {
1227            throw new NullPointerException( "unmarshaller" );
1228        }
1229        if ( javaClass == null )
1230        {
1231            throw new NullPointerException( "javaClass" );
1232        }
1233        if ( transformers == null )
1234        {
1235            throw new NullPointerException( "transformers" );
1236        }
1237 
1238        try
1239        {
1240            Specification decodedSpecification = null;
1241            final ObjectFactory objectFactory = new ObjectFactory();
1242            final byte[] bytes = this.getClassfileAttribute( javaClass, Specification.class.getName() );
1243            if ( bytes != null )
1244            {
1245                decodedSpecification = this.decodeModelObject( unmarshaller, bytes, Specification.class );
1246            }
1247 
1248            if ( decodedSpecification != null )
1249            {
1250                for ( Transformer transformer : transformers )
1251                {
1252                    final JAXBSource source =
1253                        new JAXBSource( marshaller, objectFactory.createSpecification( decodedSpecification ) );
1254 
1255                    final JAXBResult result = new JAXBResult( unmarshaller );
1256                    transformer.transform( source, result );
1257                    decodedSpecification = ( (JAXBElement<Specification>) result.getResult() ).getValue();
1258                }
1259 
1260                this.setClassfileAttribute( javaClass, Specification.class.getName(), this.encodeModelObject(
1261                    marshaller, objectFactory.createSpecification( decodedSpecification ) ) );
1262 
1263            }
1264        }
1265        catch ( final JAXBException e )
1266        {
1267            throw new ToolException( e );
1268        }
1269        catch ( final TransformerException e )
1270        {
1271            throw new ToolException( e );
1272        }
1273    }
1274 
1275    /**
1276     * Transforms committed meta-data of compiled Java classes of a given implementation of the modules of the instance.
1277     *
1278     * @param implementation The implementation to process.
1279     * @param marshaller The marshaller to use for transforming classes.
1280     * @param unmarshaller The unmarshaller to use for transforming classes.
1281     * @param javaClass The java class to process.
1282     * @param transformers The transformers to use for transforming the classes.
1283     *
1284     * @throws NullPointerException if {@code implementation}, {@code marshaller}, {@code unmarshaller},
1285     * {@code javaClass} or {@code transformers} is {@code null}.
1286     * @throws ToolException if accessing class files fails.
1287     *
1288     * @see org.jomc.model.ModelContext#createMarshaller()
1289     * @see org.jomc.model.ModelContext#createUnmarshaller()
1290     */
1291    public void transformClasses( final Implementation implementation, final Marshaller marshaller,
1292                                  final Unmarshaller unmarshaller, final JavaClass javaClass,
1293                                  final List<Transformer> transformers ) throws ToolException
1294    {
1295        if ( implementation == null )
1296        {
1297            throw new NullPointerException( "implementation" );
1298        }
1299        if ( marshaller == null )
1300        {
1301            throw new NullPointerException( "marshaller" );
1302        }
1303        if ( unmarshaller == null )
1304        {
1305            throw new NullPointerException( "unmarshaller" );
1306        }
1307        if ( javaClass == null )
1308        {
1309            throw new NullPointerException( "javaClass" );
1310        }
1311        if ( transformers == null )
1312        {
1313            throw new NullPointerException( "transformers" );
1314        }
1315 
1316        try
1317        {
1318            Dependencies decodedDependencies = null;
1319            byte[] bytes = this.getClassfileAttribute( javaClass, Dependencies.class.getName() );
1320            if ( bytes != null )
1321            {
1322                decodedDependencies = this.decodeModelObject( unmarshaller, bytes, Dependencies.class );
1323            }
1324 
1325            Messages decodedMessages = null;
1326            bytes = this.getClassfileAttribute( javaClass, Messages.class.getName() );
1327            if ( bytes != null )
1328            {
1329                decodedMessages = this.decodeModelObject( unmarshaller, bytes, Messages.class );
1330            }
1331 
1332            Properties decodedProperties = null;
1333            bytes = this.getClassfileAttribute( javaClass, Properties.class.getName() );
1334            if ( bytes != null )
1335            {
1336                decodedProperties = this.decodeModelObject( unmarshaller, bytes, Properties.class );
1337            }
1338 
1339            Specifications decodedSpecifications = null;
1340            bytes = this.getClassfileAttribute( javaClass, Specifications.class.getName() );
1341            if ( bytes != null )
1342            {
1343                decodedSpecifications = this.decodeModelObject( unmarshaller, bytes, Specifications.class );
1344            }
1345 
1346            final ObjectFactory of = new ObjectFactory();
1347            for ( Transformer transformer : transformers )
1348            {
1349                if ( decodedDependencies != null )
1350                {
1351                    final JAXBSource source = new JAXBSource( marshaller, of.createDependencies( decodedDependencies ) );
1352                    final JAXBResult result = new JAXBResult( unmarshaller );
1353                    transformer.transform( source, result );
1354                    decodedDependencies = ( (JAXBElement<Dependencies>) result.getResult() ).getValue();
1355                }
1356 
1357                if ( decodedMessages != null )
1358                {
1359                    final JAXBSource source = new JAXBSource( marshaller, of.createMessages( decodedMessages ) );
1360                    final JAXBResult result = new JAXBResult( unmarshaller );
1361                    transformer.transform( source, result );
1362                    decodedMessages = ( (JAXBElement<Messages>) result.getResult() ).getValue();
1363                }
1364 
1365                if ( decodedProperties != null )
1366                {
1367                    final JAXBSource source = new JAXBSource( marshaller, of.createProperties( decodedProperties ) );
1368                    final JAXBResult result = new JAXBResult( unmarshaller );
1369                    transformer.transform( source, result );
1370                    decodedProperties = ( (JAXBElement<Properties>) result.getResult() ).getValue();
1371                }
1372 
1373                if ( decodedSpecifications != null )
1374                {
1375                    final JAXBSource source =
1376                        new JAXBSource( marshaller, of.createSpecifications( decodedSpecifications ) );
1377 
1378                    final JAXBResult result = new JAXBResult( unmarshaller );
1379                    transformer.transform( source, result );
1380                    decodedSpecifications = ( (JAXBElement<Specifications>) result.getResult() ).getValue();
1381                }
1382            }
1383 
1384            if ( decodedDependencies != null )
1385            {
1386                this.setClassfileAttribute( javaClass, Dependencies.class.getName(), this.encodeModelObject(
1387                    marshaller, of.createDependencies( decodedDependencies ) ) );
1388 
1389            }
1390 
1391            if ( decodedMessages != null )
1392            {
1393                this.setClassfileAttribute( javaClass, Messages.class.getName(), this.encodeModelObject(
1394                    marshaller, of.createMessages( decodedMessages ) ) );
1395 
1396            }
1397 
1398            if ( decodedProperties != null )
1399            {
1400                this.setClassfileAttribute( javaClass, Properties.class.getName(), this.encodeModelObject(
1401                    marshaller, of.createProperties( decodedProperties ) ) );
1402 
1403            }
1404 
1405            if ( decodedSpecifications != null )
1406            {
1407                this.setClassfileAttribute( javaClass, Specifications.class.getName(), this.encodeModelObject(
1408                    marshaller, of.createSpecifications( decodedSpecifications ) ) );
1409 
1410            }
1411        }
1412        catch ( final JAXBException e )
1413        {
1414            throw new ToolException( e );
1415        }
1416        catch ( final TransformerException e )
1417        {
1418            throw new ToolException( e );
1419        }
1420    }
1421 
1422    /**
1423     * Parses a class file.
1424     *
1425     * @param classFile The class file to parse.
1426     *
1427     * @return The parsed class file.
1428     *
1429     * @throws NullPointerException if {@code classFile} is {@code null}.
1430     * @throws ToolException if parsing {@code classFile} fails.
1431     *
1432     * @see JavaClass
1433     */
1434    public JavaClass getJavaClass( final File classFile ) throws ToolException
1435    {
1436        if ( classFile == null )
1437        {
1438            throw new NullPointerException( "classFile" );
1439        }
1440 
1441        try
1442        {
1443            return this.getJavaClass( classFile.toURI().toURL(), classFile.getName() );
1444        }
1445        catch ( final IOException e )
1446        {
1447            throw new ToolException( e );
1448        }
1449    }
1450 
1451    /**
1452     * Parses a class file.
1453     *
1454     * @param url The URL of the class file to parse.
1455     * @param className The name of the class at {@code url}.
1456     *
1457     * @return The parsed class file.
1458     *
1459     * @throws NullPointerException if {@code url} or {@code className} is {@code null}.
1460     * @throws ToolException if parsing fails.
1461     *
1462     * @see JavaClass
1463     */
1464    public JavaClass getJavaClass( final URL url, final String className ) throws ToolException
1465    {
1466        if ( url == null )
1467        {
1468            throw new NullPointerException( "url" );
1469        }
1470        if ( className == null )
1471        {
1472            throw new NullPointerException( "className" );
1473        }
1474 
1475        try
1476        {
1477            return this.getJavaClass( url.openStream(), className );
1478        }
1479        catch ( final IOException e )
1480        {
1481            throw new ToolException( e );
1482        }
1483    }
1484 
1485    /**
1486     * Parses a class file.
1487     *
1488     * @param stream The stream to read the class file from.
1489     * @param className The name of the class to read from {@code stream}.
1490     *
1491     * @return The parsed class file.
1492     *
1493     * @throws NullPointerException if {@code stream} or {@code className} is {@code null}.
1494     * @throws ToolException if parsing fails.
1495     *
1496     * @see JavaClass
1497     */
1498    public JavaClass getJavaClass( final InputStream stream, final String className ) throws ToolException
1499    {
1500        if ( stream == null )
1501        {
1502            throw new NullPointerException( "stream" );
1503        }
1504        if ( className == null )
1505        {
1506            throw new NullPointerException( "className" );
1507        }
1508 
1509        try
1510        {
1511            final ClassParser parser = new ClassParser( stream, className );
1512            final JavaClass clazz = parser.parse();
1513            stream.close();
1514            return clazz;
1515        }
1516        catch ( final IOException e )
1517        {
1518            throw new ToolException( e );
1519        }
1520    }
1521 
1522    /**
1523     * Gets an attribute from a java class.
1524     *
1525     * @param clazz The java class to get an attribute from.
1526     * @param attributeName The name of the attribute to get.
1527     *
1528     * @return The value of attribute {@code attributeName} of {@code clazz} or {@code null} if no such attribute
1529     * exists.
1530     *
1531     * @throws NullPointerException if {@code clazz} or {@code attributeName} is {@code null}.
1532     * @throws ToolException if getting the attribute fails.
1533     *
1534     * @see JavaClass#getAttributes()
1535     */
1536    public byte[] getClassfileAttribute( final JavaClass clazz, final String attributeName ) throws ToolException
1537    {
1538        if ( clazz == null )
1539        {
1540            throw new NullPointerException( "clazz" );
1541        }
1542        if ( attributeName == null )
1543        {
1544            throw new NullPointerException( "attributeName" );
1545        }
1546 
1547        final Attribute[] attributes = clazz.getAttributes();
1548 
1549        for ( int i = attributes.length - 1; i >= 0; i-- )
1550        {
1551            final Constant constant = clazz.getConstantPool().getConstant( attributes[i].getNameIndex() );
1552 
1553            if ( constant instanceof ConstantUtf8 && attributeName.equals( ( (ConstantUtf8) constant ).getBytes() ) )
1554            {
1555                final Unknown unknown = (Unknown) attributes[i];
1556                return unknown.getBytes();
1557            }
1558        }
1559 
1560        return null;
1561    }
1562 
1563    /**
1564     * Adds or updates an attribute in a java class.
1565     *
1566     * @param clazz The class to update.
1567     * @param attributeName The name of the attribute to update.
1568     * @param data The new data of the attribute to update the {@code classFile} with.
1569     *
1570     * @throws NullPointerException if {@code clazz} or {@code attributeName} is {@code null}.
1571     * @throws ToolException if updating the class file fails.
1572     *
1573     * @see JavaClass#getAttributes()
1574     */
1575    public void setClassfileAttribute( final JavaClass clazz, final String attributeName, final byte[] data )
1576        throws ToolException
1577    {
1578        if ( clazz == null )
1579        {
1580            throw new NullPointerException( "clazz" );
1581        }
1582        if ( attributeName == null )
1583        {
1584            throw new NullPointerException( "attributeName" );
1585        }
1586 
1587        /*
1588        The JavaTM Virtual Machine Specification - Second Edition - Chapter 4.1
1589 
1590        A Java virtual machine implementation is required to silently ignore any
1591        or all attributes in the attributes table of a ClassFile structure that
1592        it does not recognize. Attributes not defined in this specification are
1593        not allowed to affect the semantics of the class file, but only to
1594        provide additional descriptive information (§4.7.1).
1595         */
1596        Attribute[] attributes = clazz.getAttributes();
1597 
1598        int attributeIndex = -1;
1599        int nameIndex = -1;
1600 
1601        for ( int i = attributes.length - 1; i >= 0; i-- )
1602        {
1603            final Constant constant = clazz.getConstantPool().getConstant( attributes[i].getNameIndex() );
1604 
1605            if ( constant instanceof ConstantUtf8 && attributeName.equals( ( (ConstantUtf8) constant ).getBytes() ) )
1606            {
1607                attributeIndex = i;
1608                nameIndex = attributes[i].getNameIndex();
1609            }
1610        }
1611 
1612        if ( nameIndex == -1 )
1613        {
1614            final Constant[] pool = clazz.getConstantPool().getConstantPool();
1615            final Constant[] tmp = new Constant[ pool.length + 1 ];
1616            System.arraycopy( pool, 0, tmp, 0, pool.length );
1617            tmp[pool.length] = new ConstantUtf8( attributeName );
1618            nameIndex = pool.length;
1619            clazz.setConstantPool( new ConstantPool( tmp ) );
1620        }
1621 
1622        final Unknown unknown = new Unknown( nameIndex, data.length, data, clazz.getConstantPool() );
1623 
1624        if ( attributeIndex == -1 )
1625        {
1626            final Attribute[] tmp = new Attribute[ attributes.length + 1 ];
1627            System.arraycopy( attributes, 0, tmp, 0, attributes.length );
1628            tmp[attributes.length] = unknown;
1629            attributes = tmp;
1630        }
1631        else
1632        {
1633            attributes[attributeIndex] = unknown;
1634        }
1635 
1636        clazz.setAttributes( attributes );
1637    }
1638 
1639    /**
1640     * Encodes a model object to a byte array.
1641     *
1642     * @param marshaller The marshaller to use for encoding the object.
1643     * @param modelObject The model object to encode.
1644     *
1645     * @return GZIP compressed XML document for {@code modelObject}.
1646     *
1647     * @throws NullPointerException if {@code marshaller} or {@code modelObject} is {@code null}.
1648     * @throws ToolException if encoding {@code modelObject} fails.
1649     */
1650    public byte[] encodeModelObject( final Marshaller marshaller, final JAXBElement<? extends ModelObject> modelObject )
1651        throws ToolException
1652    {
1653        if ( marshaller == null )
1654        {
1655            throw new NullPointerException( "marshaller" );
1656        }
1657        if ( modelObject == null )
1658        {
1659            throw new NullPointerException( "modelObject" );
1660        }
1661 
1662        try
1663        {
1664            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
1665            final GZIPOutputStream out = new GZIPOutputStream( baos );
1666            marshaller.marshal( modelObject, out );
1667            out.close();
1668            return baos.toByteArray();
1669        }
1670        catch ( final JAXBException e )
1671        {
1672            throw new ToolException( e );
1673        }
1674        catch ( final IOException e )
1675        {
1676            throw new ToolException( e );
1677        }
1678    }
1679 
1680    /**
1681     * Decodes a model object from a byte array.
1682     *
1683     * @param unmarshaller The unmarshaller to use for decoding the object.
1684     * @param bytes The encoded model object to decode.
1685     * @param type The type of the encoded model object.
1686     * @param <T> The type of the decoded model object.
1687     *
1688     * @return Model object decoded from {@code bytes}.
1689     *
1690     * @throws NullPointerException if {@code unmarshaller}, {@code bytes} or {@code type} is {@code null}.
1691     * @throws ToolException if decoding {@code bytes} fails.
1692     */
1693    public <T extends ModelObject> T decodeModelObject( final Unmarshaller unmarshaller, final byte[] bytes,
1694                                                        final Class<T> type ) throws ToolException
1695    {
1696        if ( unmarshaller == null )
1697        {
1698            throw new NullPointerException( "unmarshaller" );
1699        }
1700        if ( bytes == null )
1701        {
1702            throw new NullPointerException( "bytes" );
1703        }
1704        if ( type == null )
1705        {
1706            throw new NullPointerException( "type" );
1707        }
1708 
1709        try
1710        {
1711            final ByteArrayInputStream bais = new ByteArrayInputStream( bytes );
1712            final GZIPInputStream in = new GZIPInputStream( bais );
1713            final JAXBElement<T> element = (JAXBElement<T>) unmarshaller.unmarshal( in );
1714            in.close();
1715            return element.getValue();
1716        }
1717        catch ( final JAXBException e )
1718        {
1719            throw new ToolException( e );
1720        }
1721        catch ( final IOException e )
1722        {
1723            throw new ToolException( e );
1724        }
1725    }
1726 
1727    private String getMessage( final String key, final Object args )
1728    {
1729        final ResourceBundle b = ResourceBundle.getBundle( JavaClasses.class.getName().replace( '.', '/' ) );
1730        final MessageFormat f = new MessageFormat( b.getString( key ) );
1731        return f.format( args );
1732    }
1733 
1734}

[all classes][org.jomc.tools]
EMMA 2.0.5312 (C) Vladimir Roubtsov