View Javadoc

1   /*
2    *   Copyright (C) Christian Schulte, 2005-206
3    *   All rights reserved.
4    *
5    *   Redistribution and use in source and binary forms, with or without
6    *   modification, are permitted provided that the following conditions
7    *   are met:
8    *
9    *     o Redistributions of source code must retain the above copyright
10   *       notice, this list of conditions and the following disclaimer.
11   *
12   *     o Redistributions in binary form must reproduce the above copyright
13   *       notice, this list of conditions and the following disclaimer in
14   *       the documentation and/or other materials provided with the
15   *       distribution.
16   *
17   *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
18   *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19   *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
20   *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
21   *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22   *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23   *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24   *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25   *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26   *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27   *
28   *   $JOMC: DefaultModelContext.java 4654 2012-11-15 22:28:26Z schulte $
29   *
30   */
31  package org.jomc.modlet;
32  
33  import java.io.BufferedReader;
34  import java.io.File;
35  import java.io.FileInputStream;
36  import java.io.IOException;
37  import java.io.InputStream;
38  import java.io.InputStreamReader;
39  import java.io.Reader;
40  import java.lang.ref.Reference;
41  import java.lang.ref.SoftReference;
42  import java.lang.reflect.InvocationTargetException;
43  import java.lang.reflect.Method;
44  import java.lang.reflect.Modifier;
45  import java.net.URI;
46  import java.net.URISyntaxException;
47  import java.net.URL;
48  import java.text.MessageFormat;
49  import java.util.ArrayList;
50  import java.util.Collection;
51  import java.util.Comparator;
52  import java.util.Enumeration;
53  import java.util.HashMap;
54  import java.util.HashSet;
55  import java.util.List;
56  import java.util.Map;
57  import java.util.ResourceBundle;
58  import java.util.Set;
59  import java.util.StringTokenizer;
60  import java.util.TreeMap;
61  import java.util.jar.Attributes;
62  import java.util.jar.Manifest;
63  import java.util.logging.Level;
64  import javax.xml.XMLConstants;
65  import javax.xml.bind.JAXBContext;
66  import javax.xml.bind.JAXBException;
67  import javax.xml.bind.Marshaller;
68  import javax.xml.bind.Unmarshaller;
69  import javax.xml.transform.Source;
70  import javax.xml.transform.sax.SAXSource;
71  import javax.xml.validation.SchemaFactory;
72  import javax.xml.validation.Validator;
73  import org.w3c.dom.ls.LSInput;
74  import org.w3c.dom.ls.LSResourceResolver;
75  import org.xml.sax.EntityResolver;
76  import org.xml.sax.ErrorHandler;
77  import org.xml.sax.InputSource;
78  import org.xml.sax.SAXException;
79  import org.xml.sax.SAXParseException;
80  import org.xml.sax.helpers.DefaultHandler;
81  
82  /**
83   * Default {@code ModelContext} implementation.
84   *
85   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
86   * @version $JOMC: DefaultModelContext.java 4654 2012-11-15 22:28:26Z schulte $
87   * @see ModelContextFactory
88   */
89  public class DefaultModelContext extends ModelContext
90  {
91  
92      /**
93       * Constant for the name of the model context attribute backing property {@code providerLocation}.
94       * @see #getProviderLocation()
95       * @see ModelContext#getAttribute(java.lang.String)
96       * @since 1.2
97       */
98      public static final String PROVIDER_LOCATION_ATTRIBUTE_NAME =
99          "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  */
2184 class 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  */
2302 class 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  */
2359 class 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 }