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: DefaultModelContext.java 4654 2012-11-15 22:28:26Z schulte $ 029 * 030 */ 031package org.jomc.modlet; 032 033import java.io.BufferedReader; 034import java.io.File; 035import java.io.FileInputStream; 036import java.io.IOException; 037import java.io.InputStream; 038import java.io.InputStreamReader; 039import java.io.Reader; 040import java.lang.ref.Reference; 041import java.lang.ref.SoftReference; 042import java.lang.reflect.InvocationTargetException; 043import java.lang.reflect.Method; 044import java.lang.reflect.Modifier; 045import java.net.URI; 046import java.net.URISyntaxException; 047import java.net.URL; 048import java.text.MessageFormat; 049import java.util.ArrayList; 050import java.util.Collection; 051import java.util.Comparator; 052import java.util.Enumeration; 053import java.util.HashMap; 054import java.util.HashSet; 055import java.util.List; 056import java.util.Map; 057import java.util.ResourceBundle; 058import java.util.Set; 059import java.util.StringTokenizer; 060import java.util.TreeMap; 061import java.util.jar.Attributes; 062import java.util.jar.Manifest; 063import java.util.logging.Level; 064import javax.xml.XMLConstants; 065import javax.xml.bind.JAXBContext; 066import javax.xml.bind.JAXBException; 067import javax.xml.bind.Marshaller; 068import javax.xml.bind.Unmarshaller; 069import javax.xml.transform.Source; 070import javax.xml.transform.sax.SAXSource; 071import javax.xml.validation.SchemaFactory; 072import javax.xml.validation.Validator; 073import org.w3c.dom.ls.LSInput; 074import org.w3c.dom.ls.LSResourceResolver; 075import org.xml.sax.EntityResolver; 076import org.xml.sax.ErrorHandler; 077import org.xml.sax.InputSource; 078import org.xml.sax.SAXException; 079import org.xml.sax.SAXParseException; 080import org.xml.sax.helpers.DefaultHandler; 081 082/** 083 * Default {@code ModelContext} implementation. 084 * 085 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 086 * @version $JOMC: DefaultModelContext.java 4654 2012-11-15 22:28:26Z schulte $ 087 * @see ModelContextFactory 088 */ 089public class DefaultModelContext extends ModelContext 090{ 091 092 /** 093 * Constant for the name of the model context attribute backing property {@code providerLocation}. 094 * @see #getProviderLocation() 095 * @see ModelContext#getAttribute(java.lang.String) 096 * @since 1.2 097 */ 098 public static final String PROVIDER_LOCATION_ATTRIBUTE_NAME = 099 "org.jomc.modlet.DefaultModelContext.providerLocationAttribute"; 100 101 /** 102 * Constant for the name of the model context attribute backing property {@code platformProviderLocation}. 103 * @see #getPlatformProviderLocation() 104 * @see ModelContext#getAttribute(java.lang.String) 105 * @since 1.2 106 */ 107 public static final String PLATFORM_PROVIDER_LOCATION_ATTRIBUTE_NAME = 108 "org.jomc.modlet.DefaultModelContext.platformProviderLocationAttribute"; 109 110 /** Supported schema name extensions. */ 111 private static final String[] SCHEMA_EXTENSIONS = new String[] 112 { 113 "xsd" 114 }; 115 116 /** 117 * Class path location searched for providers by default. 118 * @see #getDefaultProviderLocation() 119 */ 120 private static final String DEFAULT_PROVIDER_LOCATION = "META-INF/services"; 121 122 /** 123 * Location searched for platform providers by default. 124 * @see #getDefaultPlatformProviderLocation() 125 */ 126 private static final String DEFAULT_PLATFORM_PROVIDER_LOCATION = 127 new StringBuilder( 255 ).append( System.getProperty( "java.home" ) ).append( File.separator ).append( "lib" ). 128 append( File.separator ).append( "jomc.properties" ).toString(); 129 130 /** 131 * Constant for the service identifier of marshaller listener services. 132 * @since 1.2 133 */ 134 private static final String MARSHALLER_LISTENER_SERVICE = "javax.xml.bind.Marshaller.Listener"; 135 136 /** 137 * Constant for the service identifier of unmarshaller listener services. 138 * @since 1.2 139 */ 140 private static final String UNMARSHALLER_LISTENER_SERVICE = "javax.xml.bind.Unmarshaller.Listener"; 141 142 /** Default provider location. */ 143 private static volatile String defaultProviderLocation; 144 145 /** Default platform provider location. */ 146 private static volatile String defaultPlatformProviderLocation; 147 148 /** Cached schema resources. */ 149 private Reference<Set<URI>> cachedSchemaResources = new SoftReference<Set<URI>>( null ); 150 151 /** Provider location of the instance. */ 152 private String providerLocation; 153 154 /** Platform provider location of the instance. */ 155 private String platformProviderLocation; 156 157 /** 158 * Creates a new {@code DefaultModelContext} instance. 159 * @since 1.2 160 */ 161 public DefaultModelContext() 162 { 163 super(); 164 } 165 166 /** 167 * Creates a new {@code DefaultModelContext} instance taking a class loader. 168 * 169 * @param classLoader The class loader of the context. 170 */ 171 public DefaultModelContext( final ClassLoader classLoader ) 172 { 173 super( classLoader ); 174 } 175 176 /** 177 * Gets the default location searched for provider resources. 178 * <p>The default provider location is controlled by system property 179 * {@code org.jomc.modlet.DefaultModelContext.defaultProviderLocation} holding the location to search 180 * for provider resources by default. If that property is not set, the {@code META-INF/services} default is 181 * returned.</p> 182 * 183 * @return The location searched for provider resources by default. 184 * 185 * @see #setDefaultProviderLocation(java.lang.String) 186 */ 187 public static String getDefaultProviderLocation() 188 { 189 if ( defaultProviderLocation == null ) 190 { 191 defaultProviderLocation = System.getProperty( 192 "org.jomc.modlet.DefaultModelContext.defaultProviderLocation", DEFAULT_PROVIDER_LOCATION ); 193 194 } 195 196 return defaultProviderLocation; 197 } 198 199 /** 200 * Sets the default location searched for provider resources. 201 * 202 * @param value The new default location to search for provider resources or {@code null}. 203 * 204 * @see #getDefaultProviderLocation() 205 */ 206 public static void setDefaultProviderLocation( final String value ) 207 { 208 defaultProviderLocation = value; 209 } 210 211 /** 212 * Gets the location searched for provider resources. 213 * 214 * @return The location searched for provider resources. 215 * 216 * @see #getDefaultProviderLocation() 217 * @see #setProviderLocation(java.lang.String) 218 * @see #PROVIDER_LOCATION_ATTRIBUTE_NAME 219 */ 220 public final String getProviderLocation() 221 { 222 if ( this.providerLocation == null ) 223 { 224 this.providerLocation = getDefaultProviderLocation(); 225 226 if ( DEFAULT_PROVIDER_LOCATION.equals( this.providerLocation ) 227 && this.getAttribute( PROVIDER_LOCATION_ATTRIBUTE_NAME ) instanceof String ) 228 { 229 final String contextProviderLocation = (String) this.getAttribute( PROVIDER_LOCATION_ATTRIBUTE_NAME ); 230 231 if ( this.isLoggable( Level.CONFIG ) ) 232 { 233 this.log( Level.CONFIG, getMessage( "contextProviderLocationInfo", 234 contextProviderLocation ), null ); 235 } 236 237 this.providerLocation = null; 238 return contextProviderLocation; 239 } 240 else if ( this.isLoggable( Level.CONFIG ) ) 241 { 242 this.log( Level.CONFIG, getMessage( "defaultProviderLocationInfo", this.providerLocation ), null ); 243 } 244 } 245 246 return this.providerLocation; 247 } 248 249 /** 250 * Sets the location searched for provider resources. 251 * 252 * @param value The new location to search for provider resources or {@code null}. 253 * 254 * @see #getProviderLocation() 255 */ 256 public final void setProviderLocation( final String value ) 257 { 258 this.providerLocation = value; 259 } 260 261 /** 262 * Gets the default location searched for platform provider resources. 263 * <p>The default platform provider location is controlled by system property 264 * {@code org.jomc.modlet.DefaultModelContext.defaultPlatformProviderLocation} holding the location to 265 * search for platform provider resources by default. If that property is not set, the 266 * {@code <java-home>/lib/jomc.properties} default is returned.</p> 267 * 268 * @return The location searched for platform provider resources by default. 269 * 270 * @see #setDefaultPlatformProviderLocation(java.lang.String) 271 */ 272 public static String getDefaultPlatformProviderLocation() 273 { 274 if ( defaultPlatformProviderLocation == null ) 275 { 276 defaultPlatformProviderLocation = System.getProperty( 277 "org.jomc.modlet.DefaultModelContext.defaultPlatformProviderLocation", 278 DEFAULT_PLATFORM_PROVIDER_LOCATION ); 279 280 } 281 282 return defaultPlatformProviderLocation; 283 } 284 285 /** 286 * Sets the default location searched for platform provider resources. 287 * 288 * @param value The new default location to search for platform provider resources or {@code null}. 289 * 290 * @see #getDefaultPlatformProviderLocation() 291 */ 292 public static void setDefaultPlatformProviderLocation( final String value ) 293 { 294 defaultPlatformProviderLocation = value; 295 } 296 297 /** 298 * Gets the location searched for platform provider resources. 299 * 300 * @return The location searched for platform provider resources. 301 * 302 * @see #getDefaultPlatformProviderLocation() 303 * @see #setPlatformProviderLocation(java.lang.String) 304 * @see #PLATFORM_PROVIDER_LOCATION_ATTRIBUTE_NAME 305 */ 306 public final String getPlatformProviderLocation() 307 { 308 if ( this.platformProviderLocation == null ) 309 { 310 this.platformProviderLocation = getDefaultPlatformProviderLocation(); 311 312 if ( DEFAULT_PLATFORM_PROVIDER_LOCATION.equals( this.platformProviderLocation ) 313 && this.getAttribute( PLATFORM_PROVIDER_LOCATION_ATTRIBUTE_NAME ) instanceof String ) 314 { 315 final String contextPlatformProviderLocation = 316 (String) this.getAttribute( PLATFORM_PROVIDER_LOCATION_ATTRIBUTE_NAME ); 317 318 if ( this.isLoggable( Level.CONFIG ) ) 319 { 320 this.log( Level.CONFIG, getMessage( "contextPlatformProviderLocationInfo", 321 contextPlatformProviderLocation ), null ); 322 323 } 324 325 this.platformProviderLocation = null; 326 return contextPlatformProviderLocation; 327 } 328 else if ( this.isLoggable( Level.CONFIG ) ) 329 { 330 this.log( Level.CONFIG, 331 getMessage( "defaultPlatformProviderLocationInfo", this.platformProviderLocation ), null ); 332 333 } 334 } 335 336 return this.platformProviderLocation; 337 } 338 339 /** 340 * Sets the location searched for platform provider resources. 341 * 342 * @param value The new location to search for platform provider resources or {@code null}. 343 * 344 * @see #getPlatformProviderLocation() 345 */ 346 public final void setPlatformProviderLocation( final String value ) 347 { 348 this.platformProviderLocation = value; 349 } 350 351 /** 352 * {@inheritDoc} 353 * <p>This method loads {@code ModletProvider} classes setup via the platform provider configuration file and 354 * {@code <provider-location>/org.jomc.modlet.ModletProvider} resources to return a list of {@code Modlets}.</p> 355 * 356 * @see #getProviderLocation() 357 * @see #getPlatformProviderLocation() 358 * @see ModletProvider#findModlets(org.jomc.modlet.ModelContext) 359 */ 360 @Override 361 public Modlets findModlets() throws ModelException 362 { 363 final Modlets modlets = new Modlets(); 364 final Collection<ModletProvider> providers = this.loadProviders( ModletProvider.class ); 365 366 for ( ModletProvider provider : providers ) 367 { 368 if ( this.isLoggable( Level.FINER ) ) 369 { 370 this.log( Level.FINER, getMessage( "creatingModlets", provider.toString() ), null ); 371 } 372 373 final Modlets provided = provider.findModlets( this ); 374 375 if ( provided != null ) 376 { 377 if ( this.isLoggable( Level.FINEST ) ) 378 { 379 for ( Modlet m : provided.getModlet() ) 380 { 381 this.log( Level.FINEST, 382 getMessage( "modletInfo", m.getName(), m.getModel(), 383 m.getVendor() != null 384 ? m.getVendor() : getMessage( "noVendor" ), 385 m.getVersion() != null 386 ? m.getVersion() : getMessage( "noVersion" ) ), null ); 387 388 if ( m.getSchemas() != null ) 389 { 390 for ( Schema s : m.getSchemas().getSchema() ) 391 { 392 this.log( Level.FINEST, 393 getMessage( "modletSchemaInfo", m.getName(), s.getPublicId(), s.getSystemId(), 394 s.getContextId() != null 395 ? s.getContextId() : getMessage( "noContext" ), 396 s.getClasspathId() != null 397 ? s.getClasspathId() : getMessage( "noClasspathId" ) ), null ); 398 399 } 400 } 401 402 if ( m.getServices() != null ) 403 { 404 for ( Service s : m.getServices().getService() ) 405 { 406 this.log( Level.FINEST, getMessage( "modletServiceInfo", m.getName(), s.getOrdinal(), 407 s.getIdentifier(), s.getClazz() ), null ); 408 409 } 410 } 411 } 412 } 413 414 modlets.getModlet().addAll( provided.getModlet() ); 415 } 416 } 417 418 return modlets; 419 } 420 421 /** 422 * {@inheritDoc} 423 * <p>This method loads all {@code ModelProvider} service classes of the model identified by {@code model} to create 424 * a new {@code Model} instance.</p> 425 * 426 * @see #findModel(org.jomc.modlet.Model) 427 * @see ModelProvider#findModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model) 428 */ 429 @Override 430 public Model findModel( final String model ) throws ModelException 431 { 432 if ( model == null ) 433 { 434 throw new NullPointerException( "model" ); 435 } 436 437 final Model m = new Model(); 438 m.setIdentifier( model ); 439 440 return this.findModel( m ); 441 } 442 443 /** 444 * {@inheritDoc} 445 * <p>This method loads all {@code ModelProvider} service classes of the given model to populate the given model 446 * instance.</p> 447 * 448 * @see #createServiceObject(org.jomc.modlet.Service, java.lang.Class) 449 * @see ModelProvider#findModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model) 450 * 451 * @since 1.2 452 */ 453 @Override 454 public Model findModel( final Model model ) throws ModelException 455 { 456 if ( model == null ) 457 { 458 throw new NullPointerException( "model" ); 459 } 460 461 Model m = model.clone(); 462 final Services services = this.getModlets().getServices( m.getIdentifier() ); 463 464 if ( services != null ) 465 { 466 for ( Service service : services.getServices( ModelProvider.class ) ) 467 { 468 final ModelProvider modelProvider = this.createServiceObject( service, ModelProvider.class ); 469 470 if ( this.isLoggable( Level.FINER ) ) 471 { 472 this.log( Level.FINER, getMessage( "creatingModel", m.getIdentifier(), modelProvider.toString() ), 473 null ); 474 475 } 476 477 final Model provided = modelProvider.findModel( this, m ); 478 479 if ( provided != null ) 480 { 481 m = provided; 482 } 483 } 484 } 485 486 return m; 487 } 488 489 /** 490 * {@inheritDoc} 491 * @since 1.2 492 */ 493 @Override 494 public <T> T createServiceObject( final Service service, final Class<T> type ) throws ModelException 495 { 496 if ( service == null ) 497 { 498 throw new NullPointerException( "service" ); 499 } 500 if ( type == null ) 501 { 502 throw new NullPointerException( "type" ); 503 } 504 505 try 506 { 507 final Class<?> clazz = this.findClass( service.getClazz() ); 508 509 if ( clazz == null ) 510 { 511 throw new ModelException( getMessage( "serviceNotFound", service.getOrdinal(), service.getIdentifier(), 512 service.getClazz() ) ); 513 514 } 515 516 if ( !type.isAssignableFrom( clazz ) ) 517 { 518 throw new ModelException( getMessage( "illegalService", service.getOrdinal(), service.getIdentifier(), 519 service.getClazz(), type.getName() ) ); 520 521 } 522 523 final T serviceObject = clazz.asSubclass( type ).newInstance(); 524 525 for ( int i = 0, s0 = service.getProperty().size(); i < s0; i++ ) 526 { 527 final Property p = service.getProperty().get( i ); 528 this.setProperty( serviceObject, p.getName(), p.getValue() ); 529 } 530 531 return serviceObject; 532 } 533 catch ( final InstantiationException e ) 534 { 535 throw new ModelException( getMessage( "failedCreatingObject", service.getClazz() ), e ); 536 } 537 catch ( final IllegalAccessException e ) 538 { 539 throw new ModelException( getMessage( "failedCreatingObject", service.getClazz() ), e ); 540 } 541 } 542 543 @Override 544 public EntityResolver createEntityResolver( final String model ) throws ModelException 545 { 546 if ( model == null ) 547 { 548 throw new NullPointerException( "model" ); 549 } 550 551 return this.createEntityResolver( this.getModlets().getSchemas( model ) ); 552 } 553 554 @Override 555 public EntityResolver createEntityResolver( final URI publicId ) throws ModelException 556 { 557 if ( publicId == null ) 558 { 559 throw new NullPointerException( "publicId" ); 560 } 561 562 return this.createEntityResolver( this.getModlets().getSchemas( publicId ) ); 563 } 564 565 @Override 566 public LSResourceResolver createResourceResolver( final String model ) throws ModelException 567 { 568 if ( model == null ) 569 { 570 throw new NullPointerException( "model" ); 571 } 572 573 return this.createResourceResolver( this.createEntityResolver( model ) ); 574 } 575 576 @Override 577 public LSResourceResolver createResourceResolver( final URI publicId ) throws ModelException 578 { 579 if ( publicId == null ) 580 { 581 throw new NullPointerException( "publicId" ); 582 } 583 584 return this.createResourceResolver( this.createEntityResolver( publicId ) ); 585 } 586 587 @Override 588 public javax.xml.validation.Schema createSchema( final String model ) throws ModelException 589 { 590 if ( model == null ) 591 { 592 throw new NullPointerException( "model" ); 593 } 594 595 return this.createSchema( this.getModlets().getSchemas( model ), this.createEntityResolver( model ), 596 this.createResourceResolver( model ), model, null ); 597 598 } 599 600 @Override 601 public javax.xml.validation.Schema createSchema( final URI publicId ) throws ModelException 602 { 603 if ( publicId == null ) 604 { 605 throw new NullPointerException( "publicId" ); 606 } 607 608 return this.createSchema( this.getModlets().getSchemas( publicId ), this.createEntityResolver( publicId ), 609 this.createResourceResolver( publicId ), null, publicId ); 610 611 } 612 613 @Override 614 public JAXBContext createContext( final String model ) throws ModelException 615 { 616 if ( model == null ) 617 { 618 throw new NullPointerException( "model" ); 619 } 620 621 return this.createContext( this.getModlets().getSchemas( model ), model, null ); 622 } 623 624 @Override 625 public JAXBContext createContext( final URI publicId ) throws ModelException 626 { 627 if ( publicId == null ) 628 { 629 throw new NullPointerException( "publicId" ); 630 } 631 632 return this.createContext( this.getModlets().getSchemas( publicId ), null, publicId ); 633 } 634 635 @Override 636 public Marshaller createMarshaller( final String model ) throws ModelException 637 { 638 if ( model == null ) 639 { 640 throw new NullPointerException( "model" ); 641 } 642 643 return this.createMarshaller( this.getModlets().getSchemas( model ), this.getModlets().getServices( model ), 644 model, null ); 645 646 } 647 648 @Override 649 public Marshaller createMarshaller( final URI publicId ) throws ModelException 650 { 651 if ( publicId == null ) 652 { 653 throw new NullPointerException( "publicId" ); 654 } 655 656 return this.createMarshaller( this.getModlets().getSchemas( publicId ), null, null, publicId ); 657 } 658 659 @Override 660 public Unmarshaller createUnmarshaller( final String model ) throws ModelException 661 { 662 if ( model == null ) 663 { 664 throw new NullPointerException( "model" ); 665 } 666 667 return this.createUnmarshaller( this.getModlets().getSchemas( model ), this.getModlets().getServices( model ), 668 model, null ); 669 670 } 671 672 @Override 673 public Unmarshaller createUnmarshaller( final URI publicId ) throws ModelException 674 { 675 if ( publicId == null ) 676 { 677 throw new NullPointerException( "publicId" ); 678 } 679 680 return this.createUnmarshaller( this.getModlets().getSchemas( publicId ), null, null, publicId ); 681 } 682 683 /** 684 * {@inheritDoc} 685 * <p>This method loads all {@code ModelProcessor} service classes of {@code model} to process the given 686 * {@code Model}.</p> 687 * 688 * @see #createServiceObject(org.jomc.modlet.Service, java.lang.Class) 689 * @see ModelProcessor#processModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model) 690 */ 691 @Override 692 public Model processModel( final Model model ) throws ModelException 693 { 694 if ( model == null ) 695 { 696 throw new NullPointerException( "model" ); 697 } 698 699 Model processed = model; 700 final Services services = this.getModlets().getServices( model.getIdentifier() ); 701 702 if ( services != null ) 703 { 704 for ( Service service : services.getServices( ModelProcessor.class ) ) 705 { 706 final ModelProcessor modelProcessor = this.createServiceObject( service, ModelProcessor.class ); 707 708 if ( this.isLoggable( Level.FINER ) ) 709 { 710 this.log( Level.FINER, getMessage( "processingModel", model.getIdentifier(), 711 modelProcessor.toString() ), null ); 712 713 } 714 715 final Model current = modelProcessor.processModel( this, processed ); 716 717 if ( current != null ) 718 { 719 processed = current; 720 } 721 } 722 } 723 724 return processed; 725 } 726 727 /** 728 * {@inheritDoc} 729 * <p>This method loads all {@code ModelValidator} service classes of {@code model} to validate the given 730 * {@code Model}.</p> 731 * 732 * @see #createServiceObject(org.jomc.modlet.Service, java.lang.Class) 733 * @see ModelValidator#validateModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model) 734 */ 735 @Override 736 public ModelValidationReport validateModel( final Model model ) throws ModelException 737 { 738 if ( model == null ) 739 { 740 throw new NullPointerException( "model" ); 741 } 742 743 final Services services = this.getModlets().getServices( model.getIdentifier() ); 744 final ModelValidationReport report = new ModelValidationReport(); 745 746 if ( services != null ) 747 { 748 for ( Service service : services.getServices( ModelValidator.class ) ) 749 { 750 final ModelValidator modelValidator = this.createServiceObject( service, ModelValidator.class ); 751 752 if ( this.isLoggable( Level.FINER ) ) 753 { 754 this.log( Level.FINER, getMessage( "validatingModel", model.getIdentifier(), 755 modelValidator.toString() ), null ); 756 757 } 758 759 final ModelValidationReport current = modelValidator.validateModel( this, model ); 760 761 if ( current != null ) 762 { 763 report.getDetails().addAll( current.getDetails() ); 764 } 765 } 766 } 767 768 return report; 769 } 770 771 /** 772 * {@inheritDoc} 773 * 774 * @see #createSchema(java.lang.String) 775 */ 776 @Override 777 public ModelValidationReport validateModel( final String model, final Source source ) throws ModelException 778 { 779 if ( model == null ) 780 { 781 throw new NullPointerException( "model" ); 782 } 783 if ( source == null ) 784 { 785 throw new NullPointerException( "source" ); 786 } 787 788 final javax.xml.validation.Schema schema = this.createSchema( model ); 789 final Validator validator = schema.newValidator(); 790 final ModelErrorHandler modelErrorHandler = new ModelErrorHandler( this ); 791 validator.setErrorHandler( modelErrorHandler ); 792 793 try 794 { 795 validator.validate( source ); 796 } 797 catch ( final SAXException e ) 798 { 799 String message = getMessage( e ); 800 if ( message == null && e.getException() != null ) 801 { 802 message = getMessage( e.getException() ); 803 } 804 805 if ( this.isLoggable( Level.FINE ) ) 806 { 807 this.log( Level.FINE, message, e ); 808 } 809 810 if ( modelErrorHandler.getReport().isModelValid() ) 811 { 812 throw new ModelException( message, e ); 813 } 814 } 815 catch ( final IOException e ) 816 { 817 throw new ModelException( getMessage( e ), e ); 818 } 819 820 return modelErrorHandler.getReport(); 821 } 822 823 private <T> Collection<T> loadProviders( final Class<T> providerClass ) throws ModelException 824 { 825 try 826 { 827 final String providerNamePrefix = providerClass.getName() + "."; 828 final Map<String, T> providers = new TreeMap<String, T>( new Comparator<String>() 829 { 830 831 public int compare( final String key1, final String key2 ) 832 { 833 return key1.compareTo( key2 ); 834 } 835 836 } ); 837 838 final File platformProviders = new File( this.getPlatformProviderLocation() ); 839 840 if ( platformProviders.exists() ) 841 { 842 if ( this.isLoggable( Level.FINEST ) ) 843 { 844 this.log( Level.FINEST, getMessage( "processing", platformProviders.getAbsolutePath() ), null ); 845 } 846 847 InputStream in = null; 848 boolean suppressExceptionOnClose = true; 849 final java.util.Properties p = new java.util.Properties(); 850 851 try 852 { 853 in = new FileInputStream( platformProviders ); 854 p.load( in ); 855 suppressExceptionOnClose = false; 856 } 857 finally 858 { 859 try 860 { 861 if ( in != null ) 862 { 863 in.close(); 864 } 865 } 866 catch ( final IOException e ) 867 { 868 if ( suppressExceptionOnClose ) 869 { 870 this.log( Level.SEVERE, getMessage( e ), e ); 871 } 872 else 873 { 874 throw e; 875 } 876 } 877 } 878 879 for ( Map.Entry<Object, Object> e : p.entrySet() ) 880 { 881 if ( e.getKey().toString().startsWith( providerNamePrefix ) ) 882 { 883 final String configuration = e.getValue().toString(); 884 885 if ( this.isLoggable( Level.FINEST ) ) 886 { 887 this.log( Level.FINEST, getMessage( "providerInfo", platformProviders.getAbsolutePath(), 888 providerClass.getName(), configuration ), null ); 889 890 } 891 892 providers.put( e.getKey().toString(), 893 this.createProviderObject( providerClass, configuration, 894 platformProviders.toURI().toURL() ) ); 895 896 } 897 } 898 } 899 900 final Enumeration<URL> classpathProviders = 901 this.findResources( this.getProviderLocation() + '/' + providerClass.getName() ); 902 903 int count = 0; 904 final long t0 = System.currentTimeMillis(); 905 906 while ( classpathProviders.hasMoreElements() ) 907 { 908 count++; 909 final URL url = classpathProviders.nextElement(); 910 911 if ( this.isLoggable( Level.FINEST ) ) 912 { 913 this.log( Level.FINEST, getMessage( "processing", url.toExternalForm() ), null ); 914 } 915 916 BufferedReader reader = null; 917 boolean suppressExceptionOnClose = true; 918 919 try 920 { 921 reader = new BufferedReader( new InputStreamReader( url.openStream(), "UTF-8" ) ); 922 923 String line = null; 924 while ( ( line = reader.readLine() ) != null ) 925 { 926 if ( line.contains( "#" ) ) 927 { 928 continue; 929 } 930 931 if ( this.isLoggable( Level.FINEST ) ) 932 { 933 this.log( Level.FINEST, getMessage( "providerInfo", url.toExternalForm(), 934 providerClass.getName(), line ), null ); 935 936 } 937 938 providers.put( providerNamePrefix + providers.size(), 939 this.createProviderObject( providerClass, line, url ) ); 940 941 } 942 943 suppressExceptionOnClose = false; 944 } 945 finally 946 { 947 try 948 { 949 if ( reader != null ) 950 { 951 reader.close(); 952 } 953 } 954 catch ( final IOException e ) 955 { 956 if ( suppressExceptionOnClose ) 957 { 958 this.log( Level.SEVERE, getMessage( e ), e ); 959 } 960 else 961 { 962 throw new ModelException( getMessage( e ), e ); 963 } 964 } 965 } 966 } 967 968 if ( this.isLoggable( Level.FINE ) ) 969 { 970 this.log( Level.FINE, getMessage( "contextReport", count, 971 this.getProviderLocation() + '/' + providerClass.getName(), 972 Long.valueOf( System.currentTimeMillis() - t0 ) ), null ); 973 974 } 975 976 return providers.values(); 977 } 978 catch ( final IOException e ) 979 { 980 throw new ModelException( getMessage( e ), e ); 981 } 982 } 983 984 private <T> T createProviderObject( final Class<T> providerClass, final String configuration, final URL location ) 985 throws ModelException 986 { 987 String className = configuration; 988 989 try 990 { 991 final Map<String, String> properties = new HashMap<String, String>(); 992 final int i0 = configuration.indexOf( '[' ); 993 final int i1 = configuration.lastIndexOf( ']' ); 994 995 if ( i0 != -1 && i1 != -1 ) 996 { 997 className = configuration.substring( 0, i0 ); 998 final StringTokenizer propertyTokens = 999 new StringTokenizer( configuration.substring( i0 + 1, i1 ), "," ); 1000 1001 while ( propertyTokens.hasMoreTokens() ) 1002 { 1003 final String property = propertyTokens.nextToken(); 1004 final int d0 = property.indexOf( '=' ); 1005 1006 String propertyName = property; 1007 String propertyValue = null; 1008 1009 if ( d0 != -1 ) 1010 { 1011 propertyName = property.substring( 0, d0 ); 1012 propertyValue = property.substring( d0 + 1, property.length() ); 1013 } 1014 1015 properties.put( propertyName, propertyValue ); 1016 } 1017 } 1018 1019 final Class<?> provider = this.findClass( className ); 1020 1021 if ( provider == null ) 1022 { 1023 throw new ModelException( getMessage( "implementationNotFound", providerClass.getName(), className, 1024 location.toExternalForm() ) ); 1025 1026 } 1027 1028 if ( !providerClass.isAssignableFrom( provider ) ) 1029 { 1030 throw new ModelException( getMessage( "illegalImplementation", providerClass.getName(), className, 1031 location.toExternalForm() ) ); 1032 1033 } 1034 1035 final T o = provider.asSubclass( providerClass ).newInstance(); 1036 1037 for ( final Map.Entry<String, String> property : properties.entrySet() ) 1038 { 1039 this.setProperty( o, property.getKey(), property.getValue() ); 1040 } 1041 1042 return o; 1043 } 1044 catch ( final InstantiationException e ) 1045 { 1046 throw new ModelException( getMessage( "failedCreatingObject", className ), e ); 1047 } 1048 catch ( final IllegalAccessException e ) 1049 { 1050 throw new ModelException( getMessage( "failedCreatingObject", className ), e ); 1051 } 1052 } 1053 1054 private <T> void setProperty( final T object, final String propertyName, final String propertyValue ) 1055 throws ModelException 1056 { 1057 if ( object == null ) 1058 { 1059 throw new NullPointerException( "object" ); 1060 } 1061 if ( propertyName == null ) 1062 { 1063 throw new NullPointerException( "propertyName" ); 1064 } 1065 1066 try 1067 { 1068 final char[] chars = propertyName.toCharArray(); 1069 1070 if ( Character.isLowerCase( chars[0] ) ) 1071 { 1072 chars[0] = Character.toUpperCase( chars[0] ); 1073 } 1074 1075 final String methodNameSuffix = String.valueOf( chars ); 1076 Method getterMethod = null; 1077 1078 try 1079 { 1080 getterMethod = object.getClass().getMethod( "get" + methodNameSuffix ); 1081 } 1082 catch ( final NoSuchMethodException e ) 1083 { 1084 if ( this.isLoggable( Level.FINEST ) ) 1085 { 1086 this.log( Level.FINEST, null, e ); 1087 } 1088 1089 getterMethod = null; 1090 } 1091 1092 if ( getterMethod == null ) 1093 { 1094 try 1095 { 1096 getterMethod = object.getClass().getMethod( "is" + methodNameSuffix ); 1097 } 1098 catch ( final NoSuchMethodException e ) 1099 { 1100 if ( this.isLoggable( Level.FINEST ) ) 1101 { 1102 this.log( Level.FINEST, null, e ); 1103 } 1104 1105 getterMethod = null; 1106 } 1107 } 1108 1109 if ( getterMethod == null ) 1110 { 1111 throw new ModelException( getMessage( "getterMethodNotFound", object.getClass().getName(), 1112 propertyName ) ); 1113 1114 } 1115 1116 final Class<?> propertyType = getterMethod.getReturnType(); 1117 Class<?> boxedPropertyType = propertyType; 1118 Class<?> unboxedPropertyType = propertyType; 1119 1120 if ( Boolean.TYPE.equals( propertyType ) ) 1121 { 1122 boxedPropertyType = Boolean.class; 1123 } 1124 else if ( Character.TYPE.equals( propertyType ) ) 1125 { 1126 boxedPropertyType = Character.class; 1127 } 1128 else if ( Byte.TYPE.equals( propertyType ) ) 1129 { 1130 boxedPropertyType = Byte.class; 1131 } 1132 else if ( Short.TYPE.equals( propertyType ) ) 1133 { 1134 boxedPropertyType = Short.class; 1135 } 1136 else if ( Integer.TYPE.equals( propertyType ) ) 1137 { 1138 boxedPropertyType = Integer.class; 1139 } 1140 else if ( Long.TYPE.equals( propertyType ) ) 1141 { 1142 boxedPropertyType = Long.class; 1143 } 1144 else if ( Float.TYPE.equals( propertyType ) ) 1145 { 1146 boxedPropertyType = Float.class; 1147 } 1148 else if ( Double.TYPE.equals( propertyType ) ) 1149 { 1150 boxedPropertyType = Double.class; 1151 } 1152 1153 if ( Boolean.class.equals( propertyType ) ) 1154 { 1155 unboxedPropertyType = Boolean.TYPE; 1156 } 1157 else if ( Character.class.equals( propertyType ) ) 1158 { 1159 unboxedPropertyType = Character.TYPE; 1160 } 1161 else if ( Byte.class.equals( propertyType ) ) 1162 { 1163 unboxedPropertyType = Byte.TYPE; 1164 } 1165 else if ( Short.class.equals( propertyType ) ) 1166 { 1167 unboxedPropertyType = Short.TYPE; 1168 } 1169 else if ( Integer.class.equals( propertyType ) ) 1170 { 1171 unboxedPropertyType = Integer.TYPE; 1172 } 1173 else if ( Long.class.equals( propertyType ) ) 1174 { 1175 unboxedPropertyType = Long.TYPE; 1176 } 1177 else if ( Float.class.equals( propertyType ) ) 1178 { 1179 unboxedPropertyType = Float.TYPE; 1180 } 1181 else if ( Double.class.equals( propertyType ) ) 1182 { 1183 unboxedPropertyType = Double.TYPE; 1184 } 1185 1186 Method setterMethod = null; 1187 1188 try 1189 { 1190 setterMethod = object.getClass().getMethod( "set" + methodNameSuffix, boxedPropertyType ); 1191 } 1192 catch ( final NoSuchMethodException e ) 1193 { 1194 if ( this.isLoggable( Level.FINEST ) ) 1195 { 1196 this.log( Level.FINEST, null, e ); 1197 } 1198 1199 setterMethod = null; 1200 } 1201 1202 if ( setterMethod == null && !boxedPropertyType.equals( unboxedPropertyType ) ) 1203 { 1204 try 1205 { 1206 setterMethod = object.getClass().getMethod( "set" + methodNameSuffix, unboxedPropertyType ); 1207 } 1208 catch ( final NoSuchMethodException e ) 1209 { 1210 if ( this.isLoggable( Level.FINEST ) ) 1211 { 1212 this.log( Level.FINEST, null, e ); 1213 } 1214 1215 setterMethod = null; 1216 } 1217 } 1218 1219 if ( setterMethod == null ) 1220 { 1221 throw new ModelException( getMessage( "setterMethodNotFound", object.getClass().getName(), 1222 propertyName ) ); 1223 1224 } 1225 1226 if ( boxedPropertyType.equals( Character.class ) ) 1227 { 1228 if ( propertyValue == null || propertyValue.length() != 1 ) 1229 { 1230 throw new ModelException( getMessage( "unsupportedCharacterValue", object.getClass().getName(), 1231 propertyName ) ); 1232 1233 } 1234 1235 setterMethod.invoke( object, Character.valueOf( propertyValue.charAt( 0 ) ) ); 1236 return; 1237 } 1238 1239 if ( propertyValue != null ) 1240 { 1241 if ( boxedPropertyType.equals( String.class ) ) 1242 { 1243 setterMethod.invoke( object, propertyValue ); 1244 return; 1245 } 1246 1247 try 1248 { 1249 setterMethod.invoke( 1250 object, boxedPropertyType.getConstructor( String.class ).newInstance( propertyValue ) ); 1251 1252 return; 1253 } 1254 catch ( final NoSuchMethodException e ) 1255 { 1256 if ( this.isLoggable( Level.FINEST ) ) 1257 { 1258 this.log( Level.FINEST, null, e ); 1259 } 1260 } 1261 1262 try 1263 { 1264 final Method valueOf = boxedPropertyType.getMethod( "valueOf", String.class ); 1265 1266 if ( Modifier.isStatic( valueOf.getModifiers() ) 1267 && ( valueOf.getReturnType().equals( boxedPropertyType ) 1268 || valueOf.getReturnType().equals( unboxedPropertyType ) ) ) 1269 { 1270 setterMethod.invoke( object, valueOf.invoke( null, propertyValue ) ); 1271 return; 1272 } 1273 } 1274 catch ( final NoSuchMethodException e ) 1275 { 1276 if ( this.isLoggable( Level.FINEST ) ) 1277 { 1278 this.log( Level.FINEST, null, e ); 1279 } 1280 } 1281 1282 throw new ModelException( getMessage( "unsupportedPropertyType", object.getClass().getName(), 1283 propertyName, propertyType.getName() ) ); 1284 1285 } 1286 else 1287 { 1288 setterMethod.invoke( object, (Object) null ); 1289 } 1290 } 1291 catch ( final IllegalAccessException e ) 1292 { 1293 throw new ModelException( getMessage( "failedSettingProperty", propertyName, object.toString(), 1294 object.getClass().getName() ), e ); 1295 1296 } 1297 catch ( final InvocationTargetException e ) 1298 { 1299 throw new ModelException( getMessage( "failedSettingProperty", propertyName, object.toString(), 1300 object.getClass().getName() ), e ); 1301 1302 } 1303 catch ( final InstantiationException e ) 1304 { 1305 throw new ModelException( getMessage( "failedSettingProperty", propertyName, object.toString(), 1306 object.getClass().getName() ), e ); 1307 1308 } 1309 } 1310 1311 /** 1312 * Searches the context for {@code META-INF/MANIFEST.MF} resources and returns a set of URIs of entries whose names 1313 * end with a known schema extension. 1314 * 1315 * @return Set of URIs of any matching entries. 1316 * 1317 * @throws IOException if reading fails. 1318 * @throws URISyntaxException if parsing fails. 1319 * @throws ModelException if searching the context fails. 1320 */ 1321 private Set<URI> getSchemaResources() throws IOException, URISyntaxException, ModelException 1322 { 1323 Set<URI> resources = this.cachedSchemaResources.get(); 1324 1325 if ( resources == null ) 1326 { 1327 resources = new HashSet<URI>(); 1328 final long t0 = System.currentTimeMillis(); 1329 int count = 0; 1330 1331 for ( final Enumeration<URL> e = this.findResources( "META-INF/MANIFEST.MF" ); 1332 e.hasMoreElements(); ) 1333 { 1334 InputStream manifestStream = null; 1335 boolean suppressExceptionOnClose = true; 1336 1337 try 1338 { 1339 count++; 1340 final URL manifestUrl = e.nextElement(); 1341 final String externalForm = manifestUrl.toExternalForm(); 1342 final String baseUrl = externalForm.substring( 0, externalForm.indexOf( "META-INF" ) ); 1343 manifestStream = manifestUrl.openStream(); 1344 final Manifest mf = new Manifest( manifestStream ); 1345 1346 if ( this.isLoggable( Level.FINEST ) ) 1347 { 1348 this.log( Level.FINEST, getMessage( "processing", externalForm ), null ); 1349 } 1350 1351 for ( Map.Entry<String, Attributes> entry : mf.getEntries().entrySet() ) 1352 { 1353 for ( int i = SCHEMA_EXTENSIONS.length - 1; i >= 0; i-- ) 1354 { 1355 if ( entry.getKey().toLowerCase().endsWith( '.' + SCHEMA_EXTENSIONS[i].toLowerCase() ) ) 1356 { 1357 final URL schemaUrl = new URL( baseUrl + entry.getKey() ); 1358 resources.add( schemaUrl.toURI() ); 1359 1360 if ( this.isLoggable( Level.FINEST ) ) 1361 { 1362 this.log( Level.FINEST, getMessage( "foundSchemaCandidate", 1363 schemaUrl.toExternalForm() ), null ); 1364 1365 } 1366 } 1367 } 1368 } 1369 1370 suppressExceptionOnClose = false; 1371 } 1372 finally 1373 { 1374 try 1375 { 1376 if ( manifestStream != null ) 1377 { 1378 manifestStream.close(); 1379 } 1380 } 1381 catch ( final IOException ex ) 1382 { 1383 if ( suppressExceptionOnClose ) 1384 { 1385 this.log( Level.SEVERE, getMessage( ex ), ex ); 1386 } 1387 else 1388 { 1389 throw ex; 1390 } 1391 } 1392 } 1393 } 1394 1395 if ( this.isLoggable( Level.FINE ) ) 1396 { 1397 this.log( Level.FINE, getMessage( "contextReport", count, "META-INF/MANIFEST.MF", 1398 Long.valueOf( System.currentTimeMillis() - t0 ) ), null ); 1399 1400 } 1401 1402 this.cachedSchemaResources = new SoftReference<Set<URI>>( resources ); 1403 } 1404 1405 return resources; 1406 } 1407 1408 private EntityResolver createEntityResolver( final Schemas schemas ) 1409 { 1410 return new DefaultHandler() 1411 { 1412 1413 @Override 1414 public InputSource resolveEntity( final String publicId, final String systemId ) 1415 throws SAXException, IOException 1416 { 1417 if ( systemId == null ) 1418 { 1419 throw new NullPointerException( "systemId" ); 1420 } 1421 1422 InputSource schemaSource = null; 1423 1424 try 1425 { 1426 Schema s = null; 1427 1428 if ( schemas != null ) 1429 { 1430 s = schemas.getSchemaBySystemId( systemId ); 1431 1432 if ( s == null && publicId != null ) 1433 { 1434 try 1435 { 1436 final List<Schema> schemasByPublicId = 1437 schemas.getSchemasByPublicId( new URI( publicId ) ); 1438 1439 if ( schemasByPublicId.size() == 1 ) 1440 { 1441 s = schemasByPublicId.get( 0 ); 1442 } 1443 } 1444 catch ( final URISyntaxException e ) 1445 { 1446 if ( isLoggable( Level.WARNING ) ) 1447 { 1448 log( Level.WARNING, getMessage( "unsupportedIdUri", publicId, getMessage( e ) ), 1449 null ); 1450 1451 } 1452 1453 s = null; 1454 } 1455 } 1456 } 1457 1458 if ( s != null ) 1459 { 1460 schemaSource = new InputSource(); 1461 schemaSource.setPublicId( s.getPublicId() != null ? s.getPublicId() : publicId ); 1462 schemaSource.setSystemId( s.getSystemId() ); 1463 1464 if ( s.getClasspathId() != null ) 1465 { 1466 final URL resource = findResource( s.getClasspathId() ); 1467 1468 if ( resource != null ) 1469 { 1470 schemaSource.setSystemId( resource.toExternalForm() ); 1471 } 1472 else if ( isLoggable( Level.WARNING ) ) 1473 { 1474 log( Level.WARNING, getMessage( "resourceNotFound", s.getClasspathId() ), null ); 1475 } 1476 } 1477 1478 if ( isLoggable( Level.FINEST ) ) 1479 { 1480 log( Level.FINEST, getMessage( "resolutionInfo", publicId + ", " + systemId, 1481 schemaSource.getPublicId() + ", " 1482 + schemaSource.getSystemId() ), null ); 1483 1484 } 1485 } 1486 1487 if ( schemaSource == null ) 1488 { 1489 final URI systemUri = new URI( systemId ); 1490 String schemaName = systemUri.getPath(); 1491 1492 if ( schemaName != null ) 1493 { 1494 final int lastIndexOfSlash = schemaName.lastIndexOf( '/' ); 1495 if ( lastIndexOfSlash != -1 && lastIndexOfSlash < schemaName.length() ) 1496 { 1497 schemaName = schemaName.substring( lastIndexOfSlash + 1 ); 1498 } 1499 1500 for ( URI uri : getSchemaResources() ) 1501 { 1502 if ( uri.getSchemeSpecificPart() != null 1503 && uri.getSchemeSpecificPart().endsWith( schemaName ) ) 1504 { 1505 schemaSource = new InputSource(); 1506 schemaSource.setPublicId( publicId ); 1507 schemaSource.setSystemId( uri.toASCIIString() ); 1508 1509 if ( isLoggable( Level.FINEST ) ) 1510 { 1511 log( Level.FINEST, getMessage( "resolutionInfo", systemUri.toASCIIString(), 1512 schemaSource.getSystemId() ), null ); 1513 1514 } 1515 1516 break; 1517 } 1518 } 1519 } 1520 else 1521 { 1522 if ( isLoggable( Level.WARNING ) ) 1523 { 1524 log( Level.WARNING, getMessage( "unsupportedIdUri", systemId, 1525 systemUri.toASCIIString() ), null ); 1526 1527 } 1528 1529 schemaSource = null; 1530 } 1531 } 1532 } 1533 catch ( final URISyntaxException e ) 1534 { 1535 if ( isLoggable( Level.WARNING ) ) 1536 { 1537 log( Level.WARNING, getMessage( "unsupportedIdUri", systemId, getMessage( e ) ), null ); 1538 } 1539 1540 schemaSource = null; 1541 } 1542 catch ( final ModelException e ) 1543 { 1544 String message = getMessage( e ); 1545 if ( message == null ) 1546 { 1547 message = ""; 1548 } 1549 else if ( message.length() > 0 ) 1550 { 1551 message = " " + message; 1552 } 1553 1554 String resource = ""; 1555 if ( publicId != null ) 1556 { 1557 resource = publicId + ", "; 1558 } 1559 resource += systemId; 1560 1561 // JDK: As of JDK 6, "new IOException( message, cause )". 1562 throw (IOException) new IOException( getMessage( 1563 "failedResolving", resource, message ) ).initCause( e ); 1564 1565 } 1566 1567 return schemaSource; 1568 } 1569 1570 }; 1571 } 1572 1573 private LSResourceResolver createResourceResolver( final EntityResolver entityResolver ) 1574 { 1575 if ( entityResolver == null ) 1576 { 1577 throw new NullPointerException( "entityResolver" ); 1578 } 1579 1580 return new LSResourceResolver() 1581 { 1582 1583 public LSInput resolveResource( final String type, final String namespaceURI, final String publicId, 1584 final String systemId, final String baseURI ) 1585 { 1586 final String resolvePublicId = namespaceURI == null ? publicId : namespaceURI; 1587 final String resolveSystemId = systemId == null ? "" : systemId; 1588 1589 try 1590 { 1591 if ( XMLConstants.W3C_XML_SCHEMA_NS_URI.equals( type ) ) 1592 { 1593 final InputSource schemaSource = 1594 entityResolver.resolveEntity( resolvePublicId, resolveSystemId ); 1595 1596 if ( schemaSource != null ) 1597 { 1598 return new LSInput() 1599 { 1600 1601 public Reader getCharacterStream() 1602 { 1603 return schemaSource.getCharacterStream(); 1604 } 1605 1606 public void setCharacterStream( final Reader characterStream ) 1607 { 1608 if ( isLoggable( Level.WARNING ) ) 1609 { 1610 log( Level.WARNING, getMessage( 1611 "unsupportedOperation", "setCharacterStream", 1612 DefaultModelContext.class.getName() + ".LSResourceResolver" ), null ); 1613 1614 } 1615 } 1616 1617 public InputStream getByteStream() 1618 { 1619 return schemaSource.getByteStream(); 1620 } 1621 1622 public void setByteStream( final InputStream byteStream ) 1623 { 1624 if ( isLoggable( Level.WARNING ) ) 1625 { 1626 log( Level.WARNING, getMessage( 1627 "unsupportedOperation", "setByteStream", 1628 DefaultModelContext.class.getName() + ".LSResourceResolver" ), null ); 1629 1630 } 1631 } 1632 1633 public String getStringData() 1634 { 1635 return null; 1636 } 1637 1638 public void setStringData( final String stringData ) 1639 { 1640 if ( isLoggable( Level.WARNING ) ) 1641 { 1642 log( Level.WARNING, getMessage( 1643 "unsupportedOperation", "setStringData", 1644 DefaultModelContext.class.getName() + ".LSResourceResolver" ), null ); 1645 1646 } 1647 } 1648 1649 public String getSystemId() 1650 { 1651 return schemaSource.getSystemId(); 1652 } 1653 1654 public void setSystemId( final String systemId ) 1655 { 1656 if ( isLoggable( Level.WARNING ) ) 1657 { 1658 log( Level.WARNING, getMessage( 1659 "unsupportedOperation", "setSystemId", 1660 DefaultModelContext.class.getName() + ".LSResourceResolver" ), null ); 1661 1662 } 1663 } 1664 1665 public String getPublicId() 1666 { 1667 return schemaSource.getPublicId(); 1668 } 1669 1670 public void setPublicId( final String publicId ) 1671 { 1672 if ( isLoggable( Level.WARNING ) ) 1673 { 1674 log( Level.WARNING, getMessage( 1675 "unsupportedOperation", "setPublicId", 1676 DefaultModelContext.class.getName() + ".LSResourceResolver" ), null ); 1677 1678 } 1679 } 1680 1681 public String getBaseURI() 1682 { 1683 return baseURI; 1684 } 1685 1686 public void setBaseURI( final String baseURI ) 1687 { 1688 if ( isLoggable( Level.WARNING ) ) 1689 { 1690 log( Level.WARNING, getMessage( 1691 "unsupportedOperation", "setBaseURI", 1692 DefaultModelContext.class.getName() + ".LSResourceResolver" ), null ); 1693 1694 } 1695 } 1696 1697 public String getEncoding() 1698 { 1699 return schemaSource.getEncoding(); 1700 } 1701 1702 public void setEncoding( final String encoding ) 1703 { 1704 if ( isLoggable( Level.WARNING ) ) 1705 { 1706 log( Level.WARNING, getMessage( 1707 "unsupportedOperation", "setEncoding", 1708 DefaultModelContext.class.getName() + ".LSResourceResolver" ), null ); 1709 1710 } 1711 } 1712 1713 public boolean getCertifiedText() 1714 { 1715 return false; 1716 } 1717 1718 public void setCertifiedText( final boolean certifiedText ) 1719 { 1720 if ( isLoggable( Level.WARNING ) ) 1721 { 1722 log( Level.WARNING, getMessage( 1723 "unsupportedOperation", "setCertifiedText", 1724 DefaultModelContext.class.getName() + ".LSResourceResolver" ), null ); 1725 1726 } 1727 } 1728 1729 }; 1730 } 1731 1732 } 1733 else if ( isLoggable( Level.WARNING ) ) 1734 { 1735 log( Level.WARNING, getMessage( "unsupportedResourceType", type ), null ); 1736 } 1737 } 1738 catch ( final SAXException e ) 1739 { 1740 String message = getMessage( e ); 1741 if ( message == null && e.getException() != null ) 1742 { 1743 message = getMessage( e.getException() ); 1744 } 1745 if ( message == null ) 1746 { 1747 message = ""; 1748 } 1749 else if ( message.length() > 0 ) 1750 { 1751 message = " " + message; 1752 } 1753 1754 String resource = ""; 1755 if ( resolvePublicId != null ) 1756 { 1757 resource = resolvePublicId + ", "; 1758 } 1759 resource += resolveSystemId; 1760 1761 if ( isLoggable( Level.SEVERE ) ) 1762 { 1763 log( Level.SEVERE, getMessage( "failedResolving", resource, message ), e ); 1764 } 1765 } 1766 catch ( final IOException e ) 1767 { 1768 String message = getMessage( e ); 1769 if ( message == null ) 1770 { 1771 message = ""; 1772 } 1773 else if ( message.length() > 0 ) 1774 { 1775 message = " " + message; 1776 } 1777 1778 String resource = ""; 1779 if ( resolvePublicId != null ) 1780 { 1781 resource = resolvePublicId + ", "; 1782 } 1783 resource += resolveSystemId; 1784 1785 if ( isLoggable( Level.SEVERE ) ) 1786 { 1787 log( Level.SEVERE, getMessage( "failedResolving", resource, message ), e ); 1788 } 1789 } 1790 1791 return null; 1792 } 1793 1794 }; 1795 } 1796 1797 private javax.xml.validation.Schema createSchema( final Schemas schemas, final EntityResolver entityResolver, 1798 final LSResourceResolver resourceResolver, final String model, 1799 final URI publicId ) throws ModelException 1800 { 1801 if ( entityResolver == null ) 1802 { 1803 throw new NullPointerException( "entityResolver" ); 1804 } 1805 if ( model != null && publicId != null ) 1806 { 1807 throw new IllegalArgumentException( "model=" + model + ", publicId=" + publicId.toASCIIString() ); 1808 } 1809 1810 try 1811 { 1812 final SchemaFactory f = SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI ); 1813 final List<Source> sources = new ArrayList<Source>( schemas != null ? schemas.getSchema().size() : 0 ); 1814 1815 if ( schemas != null ) 1816 { 1817 for ( Schema s : schemas.getSchema() ) 1818 { 1819 final InputSource inputSource = entityResolver.resolveEntity( s.getPublicId(), s.getSystemId() ); 1820 1821 if ( inputSource != null ) 1822 { 1823 sources.add( new SAXSource( inputSource ) ); 1824 } 1825 } 1826 } 1827 1828 if ( sources.isEmpty() ) 1829 { 1830 if ( model != null ) 1831 { 1832 throw new ModelException( getMessage( "missingSchemasForModel", model ) ); 1833 } 1834 if ( publicId != null ) 1835 { 1836 throw new ModelException( getMessage( "missingSchemasForPublicId", publicId ) ); 1837 } 1838 } 1839 1840 f.setResourceResolver( resourceResolver ); 1841 f.setErrorHandler( new ErrorHandler() 1842 { 1843 // See http://java.net/jira/browse/JAXP-66 1844 1845 public void warning( final SAXParseException e ) throws SAXException 1846 { 1847 String message = getMessage( e ); 1848 if ( message == null && e.getException() != null ) 1849 { 1850 message = getMessage( e.getException() ); 1851 } 1852 1853 if ( isLoggable( Level.WARNING ) ) 1854 { 1855 log( Level.WARNING, message, e ); 1856 } 1857 } 1858 1859 public void error( final SAXParseException e ) throws SAXException 1860 { 1861 throw e; 1862 } 1863 1864 public void fatalError( final SAXParseException e ) throws SAXException 1865 { 1866 throw e; 1867 } 1868 1869 } ); 1870 1871 if ( this.isLoggable( Level.FINEST ) ) 1872 { 1873 final StringBuilder schemaInfo = new StringBuilder( sources.size() * 50 ); 1874 1875 for ( Source s : sources ) 1876 { 1877 schemaInfo.append( ", " ).append( s.getSystemId() ); 1878 } 1879 1880 this.log( Level.FINEST, getMessage( "creatingSchema", schemaInfo.substring( 2 ) ), null ); 1881 } 1882 1883 return f.newSchema( sources.toArray( new Source[ sources.size() ] ) ); 1884 } 1885 catch ( final IOException e ) 1886 { 1887 throw new ModelException( getMessage( e ), e ); 1888 } 1889 catch ( final SAXException e ) 1890 { 1891 String message = getMessage( e ); 1892 if ( message == null && e.getException() != null ) 1893 { 1894 message = getMessage( e.getException() ); 1895 } 1896 1897 throw new ModelException( message, e ); 1898 } 1899 } 1900 1901 private JAXBContext createContext( final Schemas schemas, final String model, final URI publicId ) 1902 throws ModelException 1903 { 1904 if ( model != null && publicId != null ) 1905 { 1906 throw new IllegalArgumentException( "model=" + model + ", publicId=" + publicId.toASCIIString() ); 1907 } 1908 1909 try 1910 { 1911 StringBuilder packageNames = null; 1912 1913 if ( schemas != null ) 1914 { 1915 packageNames = new StringBuilder( schemas.getSchema().size() * 25 ); 1916 1917 for ( Schema schema : schemas.getSchema() ) 1918 { 1919 if ( schema.getContextId() != null ) 1920 { 1921 packageNames.append( ':' ).append( schema.getContextId() ); 1922 } 1923 } 1924 } 1925 1926 if ( packageNames == null || packageNames.length() == 0 ) 1927 { 1928 if ( model != null ) 1929 { 1930 throw new ModelException( getMessage( "missingSchemasForModel", model ) ); 1931 } 1932 if ( publicId != null ) 1933 { 1934 throw new ModelException( getMessage( "missingSchemasForPublicId", publicId ) ); 1935 } 1936 } 1937 1938 if ( this.isLoggable( Level.FINEST ) ) 1939 { 1940 this.log( Level.FINEST, getMessage( "creatingContext", packageNames.substring( 1 ) ), null ); 1941 } 1942 1943 return JAXBContext.newInstance( packageNames.substring( 1 ), this.getClassLoader() ); 1944 } 1945 catch ( final JAXBException e ) 1946 { 1947 String message = getMessage( e ); 1948 if ( message == null && e.getLinkedException() != null ) 1949 { 1950 message = getMessage( e.getLinkedException() ); 1951 } 1952 1953 throw new ModelException( message, e ); 1954 } 1955 } 1956 1957 private Marshaller createMarshaller( final Schemas schemas, final Services services, final String model, 1958 final URI publicId ) throws ModelException 1959 { 1960 if ( model != null && publicId != null ) 1961 { 1962 throw new IllegalArgumentException( "model=" + model + ", publicId=" + publicId.toASCIIString() ); 1963 } 1964 1965 try 1966 { 1967 StringBuilder packageNames = null; 1968 StringBuilder schemaLocation = null; 1969 1970 if ( schemas != null ) 1971 { 1972 packageNames = new StringBuilder( schemas.getSchema().size() * 25 ); 1973 schemaLocation = new StringBuilder( schemas.getSchema().size() * 50 ); 1974 1975 for ( Schema schema : schemas.getSchema() ) 1976 { 1977 if ( schema.getContextId() != null ) 1978 { 1979 packageNames.append( ':' ).append( schema.getContextId() ); 1980 } 1981 if ( schema.getPublicId() != null && schema.getSystemId() != null ) 1982 { 1983 schemaLocation.append( ' ' ).append( schema.getPublicId() ).append( ' ' ). 1984 append( schema.getSystemId() ); 1985 1986 } 1987 } 1988 } 1989 1990 if ( packageNames == null || packageNames.length() == 0 ) 1991 { 1992 if ( model != null ) 1993 { 1994 throw new ModelException( getMessage( "missingSchemasForModel", model ) ); 1995 } 1996 if ( publicId != null ) 1997 { 1998 throw new ModelException( getMessage( "missingSchemasForPublicId", publicId ) ); 1999 } 2000 } 2001 2002 final Marshaller m = 2003 JAXBContext.newInstance( packageNames.substring( 1 ), this.getClassLoader() ).createMarshaller(); 2004 2005 if ( schemaLocation != null && schemaLocation.length() != 0 ) 2006 { 2007 m.setProperty( Marshaller.JAXB_SCHEMA_LOCATION, schemaLocation.substring( 1 ) ); 2008 } 2009 2010 MarshallerListenerList listenerList = null; 2011 2012 if ( services != null ) 2013 { 2014 for ( Service service : services.getServices( MARSHALLER_LISTENER_SERVICE ) ) 2015 { 2016 if ( listenerList == null ) 2017 { 2018 listenerList = new MarshallerListenerList(); 2019 } 2020 2021 listenerList.getListeners().add( this.createServiceObject( service, Marshaller.Listener.class ) ); 2022 } 2023 } 2024 2025 if ( listenerList != null ) 2026 { 2027 m.setListener( listenerList ); 2028 } 2029 2030 if ( this.isLoggable( Level.FINEST ) ) 2031 { 2032 if ( listenerList == null ) 2033 { 2034 this.log( Level.FINEST, getMessage( "creatingMarshaller", packageNames.substring( 1 ), 2035 schemaLocation.substring( 1 ) ), null ); 2036 2037 } 2038 else 2039 { 2040 final StringBuilder b = new StringBuilder( listenerList.getListeners().size() * 100 ); 2041 2042 for ( int i = 0, s0 = listenerList.getListeners().size(); i < s0; i++ ) 2043 { 2044 b.append( ',' ).append( listenerList.getListeners().get( i ) ); 2045 } 2046 2047 this.log( Level.FINEST, getMessage( "creatingMarshallerWithListeners", packageNames.substring( 1 ), 2048 schemaLocation.substring( 1 ), b.substring( 1 ) ), null ); 2049 2050 } 2051 } 2052 2053 return m; 2054 } 2055 catch ( final JAXBException e ) 2056 { 2057 String message = getMessage( e ); 2058 if ( message == null && e.getLinkedException() != null ) 2059 { 2060 message = getMessage( e.getLinkedException() ); 2061 } 2062 2063 throw new ModelException( message, e ); 2064 } 2065 } 2066 2067 private Unmarshaller createUnmarshaller( final Schemas schemas, final Services services, final String model, 2068 final URI publicId ) throws ModelException 2069 { 2070 if ( model != null && publicId != null ) 2071 { 2072 throw new IllegalArgumentException( "model=" + model + ", publicId=" + publicId.toASCIIString() ); 2073 } 2074 2075 try 2076 { 2077 StringBuilder packageNames = null; 2078 2079 if ( schemas != null ) 2080 { 2081 packageNames = new StringBuilder( schemas.getSchema().size() * 25 ); 2082 2083 for ( Schema schema : schemas.getSchema() ) 2084 { 2085 if ( schema.getContextId() != null ) 2086 { 2087 packageNames.append( ':' ).append( schema.getContextId() ); 2088 } 2089 } 2090 } 2091 2092 if ( packageNames == null || packageNames.length() == 0 ) 2093 { 2094 if ( model != null ) 2095 { 2096 throw new ModelException( getMessage( "missingSchemasForModel", model ) ); 2097 } 2098 if ( publicId != null ) 2099 { 2100 throw new ModelException( getMessage( "missingSchemasForPublicId", publicId ) ); 2101 } 2102 } 2103 2104 final Unmarshaller u = 2105 JAXBContext.newInstance( packageNames.substring( 1 ), this.getClassLoader() ).createUnmarshaller(); 2106 2107 UnmarshallerListenerList listenerList = null; 2108 2109 if ( services != null ) 2110 { 2111 for ( Service service : services.getServices( UNMARSHALLER_LISTENER_SERVICE ) ) 2112 { 2113 if ( listenerList == null ) 2114 { 2115 listenerList = new UnmarshallerListenerList(); 2116 } 2117 2118 listenerList.getListeners().add( this.createServiceObject( service, Unmarshaller.Listener.class ) ); 2119 } 2120 } 2121 2122 if ( listenerList != null ) 2123 { 2124 u.setListener( listenerList ); 2125 } 2126 2127 if ( this.isLoggable( Level.FINEST ) ) 2128 { 2129 if ( listenerList == null ) 2130 { 2131 this.log( Level.FINEST, 2132 getMessage( "creatingUnmarshaller", packageNames.substring( 1 ) ), null ); 2133 2134 } 2135 else 2136 { 2137 final StringBuilder b = new StringBuilder( listenerList.getListeners().size() * 100 ); 2138 2139 for ( int i = 0, s0 = listenerList.getListeners().size(); i < s0; i++ ) 2140 { 2141 b.append( ',' ).append( listenerList.getListeners().get( i ) ); 2142 } 2143 2144 this.log( Level.FINEST, getMessage( "creatingUnmarshallerWithListeners", 2145 packageNames.substring( 1 ), b.substring( 1 ) ), null ); 2146 2147 } 2148 } 2149 2150 return u; 2151 } 2152 catch ( final JAXBException e ) 2153 { 2154 String message = getMessage( e ); 2155 if ( message == null && e.getLinkedException() != null ) 2156 { 2157 message = getMessage( e.getLinkedException() ); 2158 } 2159 2160 throw new ModelException( message, e ); 2161 } 2162 } 2163 2164 private static String getMessage( final String key, final Object... arguments ) 2165 { 2166 return MessageFormat.format( ResourceBundle.getBundle( 2167 DefaultModelContext.class.getName().replace( '.', '/' ) ).getString( key ), arguments ); 2168 2169 } 2170 2171 private static String getMessage( final Throwable t ) 2172 { 2173 return t != null ? t.getMessage() != null ? t.getMessage() : getMessage( t.getCause() ) : null; 2174 } 2175 2176} 2177 2178/** 2179 * {@code ErrorHandler} collecting {@code ModelValidationReport} details. 2180 * 2181 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 2182 * @version $JOMC: DefaultModelContext.java 4654 2012-11-15 22:28:26Z schulte $ 2183 */ 2184class ModelErrorHandler extends DefaultHandler 2185{ 2186 2187 /** The context of the instance. */ 2188 private ModelContext context; 2189 2190 /** The report of the instance. */ 2191 private ModelValidationReport report; 2192 2193 /** 2194 * Creates a new {@code ModelErrorHandler} instance taking a context. 2195 * 2196 * @param context The context of the instance. 2197 */ 2198 ModelErrorHandler( final ModelContext context ) 2199 { 2200 this( context, null ); 2201 } 2202 2203 /** 2204 * Creates a new {@code ModelErrorHandler} instance taking a report to use for collecting validation events. 2205 * 2206 * @param context The context of the instance. 2207 * @param report A report to use for collecting validation events. 2208 */ 2209 ModelErrorHandler( final ModelContext context, final ModelValidationReport report ) 2210 { 2211 super(); 2212 this.context = context; 2213 this.report = report; 2214 } 2215 2216 /** 2217 * Gets the report of the instance. 2218 * 2219 * @return The report of the instance. 2220 */ 2221 public ModelValidationReport getReport() 2222 { 2223 if ( this.report == null ) 2224 { 2225 this.report = new ModelValidationReport(); 2226 } 2227 2228 return this.report; 2229 } 2230 2231 @Override 2232 public void warning( final SAXParseException exception ) throws SAXException 2233 { 2234 String message = getMessage( exception ); 2235 if ( message == null && exception.getException() != null ) 2236 { 2237 message = getMessage( exception.getException() ); 2238 } 2239 2240 if ( this.context != null && this.context.isLoggable( Level.FINE ) ) 2241 { 2242 this.context.log( Level.FINE, message, exception ); 2243 } 2244 2245 this.getReport().getDetails().add( new ModelValidationReport.Detail( 2246 "W3C XML 1.0 Recommendation - Warning condition", Level.WARNING, message, null ) ); 2247 2248 } 2249 2250 @Override 2251 public void error( final SAXParseException exception ) throws SAXException 2252 { 2253 String message = getMessage( exception ); 2254 if ( message == null && exception.getException() != null ) 2255 { 2256 message = getMessage( exception.getException() ); 2257 } 2258 2259 if ( this.context != null && this.context.isLoggable( Level.FINE ) ) 2260 { 2261 this.context.log( Level.FINE, message, exception ); 2262 } 2263 2264 this.getReport().getDetails().add( new ModelValidationReport.Detail( 2265 "W3C XML 1.0 Recommendation - Section 1.2 - Error", Level.SEVERE, message, null ) ); 2266 2267 } 2268 2269 @Override 2270 public void fatalError( final SAXParseException exception ) throws SAXException 2271 { 2272 String message = getMessage( exception ); 2273 if ( message == null && exception.getException() != null ) 2274 { 2275 message = getMessage( exception.getException() ); 2276 } 2277 2278 if ( this.context != null && this.context.isLoggable( Level.FINE ) ) 2279 { 2280 this.context.log( Level.FINE, message, exception ); 2281 } 2282 2283 this.getReport().getDetails().add( new ModelValidationReport.Detail( 2284 "W3C XML 1.0 Recommendation - Section 1.2 - Fatal Error", Level.SEVERE, message, null ) ); 2285 2286 } 2287 2288 private static String getMessage( final Throwable t ) 2289 { 2290 return t != null ? t.getMessage() != null ? t.getMessage() : getMessage( t.getCause() ) : null; 2291 } 2292 2293} 2294 2295/** 2296 * List of {@code Marshaller.Listener}s. 2297 * 2298 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 2299 * @version $JOMC: DefaultModelContext.java 4654 2012-11-15 22:28:26Z schulte $ 2300 * @since 1.2 2301 */ 2302class MarshallerListenerList extends Marshaller.Listener 2303{ 2304 2305 /** The {@code Marshaller.Listener}s of the instance. */ 2306 private List<Marshaller.Listener> listeners; 2307 2308 /** Creates a new {@code MarshallerListenerList} instance. */ 2309 MarshallerListenerList() 2310 { 2311 super(); 2312 } 2313 2314 /** 2315 * Gets the listeners of the instance. 2316 * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make 2317 * to the returned list will be present inside the object. This is why there is no {@code set} method for the 2318 * listeners property.</p> 2319 * 2320 * @return The list of listeners of the instance. 2321 */ 2322 List<Marshaller.Listener> getListeners() 2323 { 2324 if ( this.listeners == null ) 2325 { 2326 this.listeners = new ArrayList<Marshaller.Listener>(); 2327 } 2328 2329 return this.listeners; 2330 } 2331 2332 @Override 2333 public void beforeMarshal( final Object source ) 2334 { 2335 for ( int i = 0, s0 = this.getListeners().size(); i < s0; i++ ) 2336 { 2337 this.getListeners().get( i ).beforeMarshal( source ); 2338 } 2339 } 2340 2341 @Override 2342 public void afterMarshal( final Object source ) 2343 { 2344 for ( int i = 0, s0 = this.getListeners().size(); i < s0; i++ ) 2345 { 2346 this.getListeners().get( i ).afterMarshal( source ); 2347 } 2348 } 2349 2350} 2351 2352/** 2353 * List of {@code Unmarshaller.Listener}s. 2354 * 2355 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 2356 * @version $JOMC: DefaultModelContext.java 4654 2012-11-15 22:28:26Z schulte $ 2357 * @since 1.2 2358 */ 2359class UnmarshallerListenerList extends Unmarshaller.Listener 2360{ 2361 2362 /** The {@code Unmarshaller.Listener}s of the instance. */ 2363 private List<Unmarshaller.Listener> listeners; 2364 2365 /** Creates a new {@code UnmarshallerListenerList} instance. */ 2366 UnmarshallerListenerList() 2367 { 2368 super(); 2369 } 2370 2371 /** 2372 * Gets the listeners of the instance. 2373 * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make 2374 * to the returned list will be present inside the object. This is why there is no {@code set} method for the 2375 * listeners property.</p> 2376 * 2377 * @return The list of listeners of the instance. 2378 */ 2379 List<Unmarshaller.Listener> getListeners() 2380 { 2381 if ( this.listeners == null ) 2382 { 2383 this.listeners = new ArrayList<Unmarshaller.Listener>(); 2384 } 2385 2386 return this.listeners; 2387 } 2388 2389 @Override 2390 public void beforeUnmarshal( final Object target, final Object parent ) 2391 { 2392 for ( int i = 0, s0 = this.getListeners().size(); i < s0; i++ ) 2393 { 2394 this.getListeners().get( i ).beforeUnmarshal( target, parent ); 2395 } 2396 } 2397 2398 @Override 2399 public void afterUnmarshal( final Object target, final Object parent ) 2400 { 2401 for ( int i = 0, s0 = this.getListeners().size(); i < s0; i++ ) 2402 { 2403 this.getListeners().get( i ).afterUnmarshal( target, parent ); 2404 } 2405 } 2406 2407}