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