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