001 /*
002 * Copyright (c) 2009 The JOMC Project
003 * Copyright (c) 2005 Christian Schulte <cs@jomc.org>
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: JavaClasses.java 1237 2010-01-09 20:22:54Z 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 * Manages Java classes.
082 *
083 * <p><b>Use cases</b><br/><ul>
084 * <li>{@link #commitClasses(javax.xml.bind.Marshaller, java.io.File) }</li>
085 * <li>{@link #commitClasses(org.jomc.model.Module, javax.xml.bind.Marshaller, java.io.File) }</li>
086 * <li>{@link #commitClasses(org.jomc.model.Specification, javax.xml.bind.Marshaller, java.io.File) }</li>
087 * <li>{@link #commitClasses(org.jomc.model.Implementation, javax.xml.bind.Marshaller, java.io.File) }</li>
088 * <li>{@link #validateClasses(javax.xml.bind.Unmarshaller, java.io.File) }</li>
089 * <li>{@link #validateClasses(javax.xml.bind.Unmarshaller, java.lang.ClassLoader) }</li>
090 * <li>{@link #validateClasses(org.jomc.model.Module, javax.xml.bind.Unmarshaller, java.io.File) }</li>
091 * <li>{@link #validateClasses(org.jomc.model.Module, javax.xml.bind.Unmarshaller, java.lang.ClassLoader) }</li>
092 * <li>{@link #validateClasses(org.jomc.model.Specification, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) }</li>
093 * <li>{@link #validateClasses(org.jomc.model.Implementation, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass) }</li>
094 * <li>{@link #transformClasses(javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, java.io.File, java.util.List) }</li>
095 * <li>{@link #transformClasses(org.jomc.model.Module, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, java.io.File, java.util.List) }</li>
096 * <li>{@link #transformClasses(org.jomc.model.Specification, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass, java.util.List) }</li>
097 * <li>{@link #transformClasses(org.jomc.model.Implementation, javax.xml.bind.Marshaller, javax.xml.bind.Unmarshaller, org.apache.bcel.classfile.JavaClass, java.util.List) }</li>
098 * </ul></p>
099 *
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 */
105 public 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 }