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