001    /*
002     *   Copyright (c) 2009 The JOMC Project
003     *   Copyright (c) 2005 Christian Schulte <cs@jomc.org>
004     *   All rights reserved.
005     *
006     *   Redistribution and use in source and binary forms, with or without
007     *   modification, are permitted provided that the following conditions
008     *   are met:
009     *
010     *     o Redistributions of source code must retain the above copyright
011     *       notice, this list of conditions and the following disclaimer.
012     *
013     *     o Redistributions in binary form must reproduce the above copyright
014     *       notice, this list of conditions and the following disclaimer in
015     *       the documentation and/or other materials provided with the
016     *       distribution.
017     *
018     *   THIS SOFTWARE IS PROVIDED BY THE JOMC PROJECT AND CONTRIBUTORS "AS IS"
019     *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020     *   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021     *   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JOMC PROJECT OR
022     *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023     *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024     *   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025     *   OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026     *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
027     *   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
028     *   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029     *
030     *   $Id: DefaultModelManager.java 509 2009-09-21 13:54:49Z schulte2005 $
031     *
032     */
033    package org.jomc.model;
034    
035    import java.io.IOException;
036    import java.io.InputStream;
037    import java.io.Reader;
038    import java.io.StringReader;
039    import java.io.StringWriter;
040    import java.lang.reflect.Constructor;
041    import java.lang.reflect.InvocationTargetException;
042    import java.lang.reflect.Method;
043    import java.lang.reflect.Modifier;
044    import java.net.URI;
045    import java.net.URISyntaxException;
046    import java.net.URL;
047    import java.text.MessageFormat;
048    import java.util.ArrayList;
049    import java.util.Enumeration;
050    import java.util.HashMap;
051    import java.util.HashSet;
052    import java.util.Iterator;
053    import java.util.LinkedList;
054    import java.util.List;
055    import java.util.Locale;
056    import java.util.Map;
057    import java.util.ResourceBundle;
058    import java.util.Set;
059    import java.util.jar.Attributes;
060    import java.util.jar.Manifest;
061    import java.util.logging.Level;
062    import javax.xml.XMLConstants;
063    import javax.xml.bind.JAXBContext;
064    import javax.xml.bind.JAXBElement;
065    import javax.xml.bind.JAXBException;
066    import javax.xml.bind.Marshaller;
067    import javax.xml.bind.Unmarshaller;
068    import javax.xml.bind.util.JAXBResult;
069    import javax.xml.bind.util.JAXBSource;
070    import javax.xml.transform.ErrorListener;
071    import javax.xml.transform.Source;
072    import javax.xml.transform.Transformer;
073    import javax.xml.transform.TransformerConfigurationException;
074    import javax.xml.transform.TransformerException;
075    import javax.xml.transform.TransformerFactory;
076    import javax.xml.transform.URIResolver;
077    import javax.xml.transform.sax.SAXSource;
078    import javax.xml.transform.stream.StreamSource;
079    import javax.xml.validation.SchemaFactory;
080    import javax.xml.validation.Validator;
081    import org.jomc.model.bootstrap.BootstrapObject;
082    import org.jomc.model.bootstrap.Schema;
083    import org.jomc.model.bootstrap.Schemas;
084    import org.jomc.util.ParseException;
085    import org.jomc.util.TokenMgrError;
086    import org.jomc.util.VersionParser;
087    import org.jomc.util.WeakIdentityHashMap;
088    import org.w3c.dom.ls.LSInput;
089    import org.w3c.dom.ls.LSResourceResolver;
090    import org.xml.sax.EntityResolver;
091    import org.xml.sax.InputSource;
092    import org.xml.sax.SAXException;
093    
094    /**
095     * Default {@code ModelManager} implementation.
096     *
097     * <p><b>Classpath support</b><ul>
098     * <li>{@link #getClassLoader() }</li>
099     * <li>{@link #setClassLoader(java.lang.ClassLoader) }</li>
100     * <li>{@link #getDefaultDocumentLocation() }</li>
101     * <li>{@link #getClasspathModule(org.jomc.model.Modules) }</li>
102     * <li>{@link #getClasspathModules(java.lang.String) }</li>
103     * <li>{@link #getDefaultStylesheetLocation() }</li>
104     * <li>{@link #getClasspathTransformers(java.lang.String) }</li>
105     * </ul></p>
106     *
107     * <p><b>Logging</b><ul>
108     * <li>{@link #getListeners() }</li>
109     * <li>{@link #log(java.util.logging.Level, java.lang.String, java.lang.Throwable) }</li>
110     * </ul></p>
111     *
112     * <p><b>Model bootstrapping</b><ul>
113     * <li>{@link #getBootstrapContext() }</li>
114     * <li>{@link #getBootstrapDocumentLocation() }</li>
115     * <li>{@link #getBootstrapMarshaller(boolean, boolean) }</li>
116     * <li>{@link #getBootstrapObjectFactory() }</li>
117     * <li>{@link #getBootstrapSchema() }</li>
118     * <li>{@link #getBootstrapUnmarshaller(boolean) }</li>
119     * <li>{@link #validateBootstrapObject(javax.xml.bind.JAXBElement) }</li>
120     * <li>{@link #transformBootstrapObject(javax.xml.bind.JAXBElement, javax.xml.transform.Transformer) }</li>
121     * </ul></p>
122     *
123     * @author <a href="mailto:cs@jomc.org">Christian Schulte</a>
124     * @version $Id: DefaultModelManager.java 509 2009-09-21 13:54:49Z schulte2005 $
125     */
126    public class DefaultModelManager implements ModelManager
127    {
128        // SECTION-START[ModelManager]
129    
130        public ObjectFactory getObjectFactory()
131        {
132            if ( this.objectFactory == null )
133            {
134                this.objectFactory = new ObjectFactory();
135            }
136    
137            return this.objectFactory;
138        }
139    
140        public EntityResolver getEntityResolver()
141        {
142            if ( this.entityResolver == null )
143            {
144                this.entityResolver = new EntityResolver()
145                {
146    
147                    public InputSource resolveEntity( final String publicId, final String systemId )
148                        throws SAXException, IOException
149                    {
150                        if ( systemId == null )
151                        {
152                            throw new NullPointerException( "systemId" );
153                        }
154    
155                        InputSource schemaSource = null;
156    
157                        try
158                        {
159                            final Schema s = getSchemas().getSchema( publicId );
160                            if ( s != null )
161                            {
162                                schemaSource = new InputSource();
163                                schemaSource.setPublicId( publicId );
164    
165                                if ( s.getClasspathId() != null )
166                                {
167                                    schemaSource.setSystemId( getClassLoader().getResource( s.getClasspathId() ).
168                                        toExternalForm() );
169    
170                                }
171                                else
172                                {
173                                    schemaSource.setSystemId( s.getSystemId() );
174                                }
175                            }
176    
177                            if ( schemaSource == null )
178                            {
179                                final URI systemUri = new URI( systemId );
180                                String schemaName = systemUri.getPath();
181                                if ( schemaName != null )
182                                {
183                                    final int lastIndexOfSlash = schemaName.lastIndexOf( '/' );
184                                    if ( lastIndexOfSlash != -1 && lastIndexOfSlash < schemaName.length() )
185                                    {
186                                        schemaName = schemaName.substring( lastIndexOfSlash + 1 );
187                                    }
188    
189                                    for ( URL url : getSchemaResources() )
190                                    {
191                                        if ( url.getPath().endsWith( schemaName ) )
192                                        {
193                                            schemaSource = new InputSource();
194                                            schemaSource.setPublicId( publicId );
195                                            schemaSource.setSystemId( url.toExternalForm() );
196    
197                                            log( Level.FINE, getMessage( "resolvedSystemIdUri", new Object[]
198                                                {
199                                                    systemUri.toASCIIString(),
200                                                    schemaSource.getSystemId()
201                                                } ), null );
202    
203                                            break;
204                                        }
205                                    }
206                                }
207                                else
208                                {
209                                    log( Level.WARNING, getMessage( "unsupportedSystemIdUri", new Object[]
210                                        {
211                                            systemId, systemUri.toASCIIString()
212                                        } ), null );
213    
214                                    schemaSource = null;
215                                }
216                            }
217                        }
218                        catch ( final URISyntaxException e )
219                        {
220                            log( Level.WARNING, getMessage( "unsupportedSystemIdUri", new Object[]
221                                {
222                                    systemId, e.getMessage()
223                                } ), null );
224    
225                            schemaSource = null;
226                        }
227                        catch ( final JAXBException e )
228                        {
229                            throw (IOException) new IOException( e.getMessage() ).initCause( e );
230                        }
231    
232                        return schemaSource;
233                    }
234    
235                };
236            }
237    
238            return this.entityResolver;
239        }
240    
241        public LSResourceResolver getLSResourceResolver()
242        {
243            if ( this.resourceResolver == null )
244            {
245                this.resourceResolver = new LSResourceResolver()
246                {
247    
248                    public LSInput resolveResource( final String type, final String namespaceURI, final String publicId,
249                                                    final String systemId, final String baseURI )
250                    {
251                        LSInput input = null;
252                        try
253                        {
254                            final InputSource schemaSource = getEntityResolver().resolveEntity( publicId, systemId );
255    
256                            if ( schemaSource != null )
257                            {
258                                input = new LSInput()
259                                {
260    
261                                    public Reader getCharacterStream()
262                                    {
263                                        return schemaSource.getCharacterStream();
264                                    }
265    
266                                    public void setCharacterStream( final Reader characterStream )
267                                    {
268                                        throw new UnsupportedOperationException();
269                                    }
270    
271                                    public InputStream getByteStream()
272                                    {
273                                        return schemaSource.getByteStream();
274                                    }
275    
276                                    public void setByteStream( final InputStream byteStream )
277                                    {
278                                        throw new UnsupportedOperationException();
279                                    }
280    
281                                    public String getStringData()
282                                    {
283                                        return null;
284                                    }
285    
286                                    public void setStringData( final String stringData )
287                                    {
288                                        throw new UnsupportedOperationException();
289                                    }
290    
291                                    public String getSystemId()
292                                    {
293                                        return schemaSource.getSystemId();
294                                    }
295    
296                                    public void setSystemId( final String systemId )
297                                    {
298                                        throw new UnsupportedOperationException();
299                                    }
300    
301                                    public String getPublicId()
302                                    {
303                                        return schemaSource.getPublicId();
304                                    }
305    
306                                    public void setPublicId( final String publicId )
307                                    {
308                                        throw new UnsupportedOperationException();
309                                    }
310    
311                                    public String getBaseURI()
312                                    {
313                                        return null;
314                                    }
315    
316                                    public void setBaseURI( final String baseURI )
317                                    {
318                                        throw new UnsupportedOperationException();
319                                    }
320    
321                                    public String getEncoding()
322                                    {
323                                        return schemaSource.getEncoding();
324                                    }
325    
326                                    public void setEncoding( final String encoding )
327                                    {
328                                        throw new UnsupportedOperationException();
329                                    }
330    
331                                    public boolean getCertifiedText()
332                                    {
333                                        return false;
334                                    }
335    
336                                    public void setCertifiedText( final boolean certifiedText )
337                                    {
338                                        throw new UnsupportedOperationException();
339                                    }
340    
341                                };
342                            }
343    
344                        }
345                        catch ( final SAXException e )
346                        {
347                            log( Level.WARNING, getMessage( "unsupportedSystemIdUri", new Object[]
348                                {
349                                    systemId, e.getMessage()
350                                } ), null );
351    
352                            input = null;
353                        }
354                        catch ( final IOException e )
355                        {
356                            log( Level.WARNING, getMessage( "unsupportedSystemIdUri", new Object[]
357                                {
358                                    systemId, e.getMessage()
359                                } ), null );
360    
361                            input = null;
362                        }
363    
364                        return input;
365                    }
366    
367                };
368            }
369    
370            return this.resourceResolver;
371        }
372    
373        public javax.xml.validation.Schema getSchema() throws IOException, SAXException, JAXBException
374        {
375            if ( this.schema == null )
376            {
377                final SchemaFactory f = SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI );
378                final List<Source> sources = new ArrayList<Source>( this.getSchemas().getSchema().size() );
379    
380                for ( Schema s : this.getSchemas().getSchema() )
381                {
382                    sources.add( new StreamSource( this.getClassLoader().getResourceAsStream( s.getClasspathId() ),
383                                                   s.getSystemId() ) );
384    
385                }
386    
387                this.schema = f.newSchema( sources.toArray( new Source[ sources.size() ] ) );
388            }
389    
390            return this.schema;
391        }
392    
393        public JAXBContext getContext() throws IOException, SAXException, JAXBException
394        {
395            if ( this.context == null )
396            {
397                final StringBuilder pkgs = new StringBuilder();
398    
399                for ( final Iterator<Schema> s = this.getSchemas().getSchema().iterator(); s.hasNext(); )
400                {
401                    pkgs.append( s.next().getContextId() );
402                    if ( s.hasNext() )
403                    {
404                        pkgs.append( ':' );
405                    }
406                }
407    
408                if ( pkgs.length() == 0 )
409                {
410                    throw new IOException( this.getMessage( "missingSchemas", new Object[]
411                        {
412                            this.getBootstrapDocumentLocation()
413                        } ) );
414    
415                }
416    
417                this.context = JAXBContext.newInstance( pkgs.toString(), this.getClassLoader() );
418            }
419    
420            return this.context;
421        }
422    
423        public Marshaller getMarshaller( final boolean validating, final boolean formattedOutput )
424            throws IOException, SAXException, JAXBException
425        {
426            final Marshaller m = this.getContext().createMarshaller();
427            final StringBuilder schemaLocation = new StringBuilder();
428    
429            for ( final Iterator<Schema> it = this.getSchemas().getSchema().iterator(); it.hasNext(); )
430            {
431                final Schema s = it.next();
432                schemaLocation.append( s.getPublicId() ).append( ' ' ).append( s.getSystemId() );
433                if ( it.hasNext() )
434                {
435                    schemaLocation.append( ' ' );
436                }
437            }
438    
439            m.setProperty( Marshaller.JAXB_SCHEMA_LOCATION, schemaLocation.toString() );
440            m.setProperty( Marshaller.JAXB_ENCODING, "UTF-8" );
441            m.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.valueOf( formattedOutput ) );
442    
443            if ( validating )
444            {
445                m.setSchema( this.getSchema() );
446            }
447    
448            return m;
449        }
450    
451        public Unmarshaller getUnmarshaller( final boolean validating ) throws IOException, SAXException, JAXBException
452        {
453            final Unmarshaller u = this.getContext().createUnmarshaller();
454    
455            if ( validating )
456            {
457                u.setSchema( this.getSchema() );
458            }
459    
460            return u;
461        }
462    
463        public void validateModelObject( final JAXBElement<? extends ModelObject> modelObject )
464            throws ModelException, IOException, SAXException, JAXBException
465        {
466            if ( modelObject == null )
467            {
468                throw new NullPointerException( "modelObject" );
469            }
470    
471            final StringWriter stringWriter = new StringWriter();
472            final Validator validator = this.getSchema().newValidator();
473            final ModelExceptionErrorHandler errorHandler = new ModelExceptionErrorHandler();
474            validator.setErrorHandler( errorHandler );
475            this.getMarshaller( false, false ).marshal( modelObject, stringWriter );
476    
477            try
478            {
479                validator.validate( new StreamSource( new StringReader( stringWriter.toString() ) ) );
480            }
481            catch ( final SAXException e )
482            {
483                final ModelException modelException = new ModelException( e.getMessage(), e );
484                modelException.getDetails().addAll( errorHandler.getDetails() );
485                throw modelException;
486            }
487        }
488    
489        public void validateModules( final Modules modules )
490            throws ModelException, IOException, SAXException, JAXBException
491        {
492            if ( modules == null )
493            {
494                throw new NullPointerException( "modules" );
495            }
496    
497            this.validateModelObject( this.getObjectFactory().createModules( modules ) );
498            final List<ModelException.Detail> details = new LinkedList<ModelException.Detail>();
499    
500            try
501            {
502                for ( Module m : modules.getModule() )
503                {
504                    if ( m.getMessages() != null )
505                    {
506                        this.assertMessagesUniqueness( m.getMessages(), details );
507                    }
508    
509                    if ( m.getProperties() != null )
510                    {
511                        this.assertPropertiesUniqueness( m.getProperties(), details );
512                    }
513    
514                    if ( m.getImplementations() != null )
515                    {
516                        for ( Implementation i : m.getImplementations().getImplementation() )
517                        {
518                            if ( i.getMessages() != null )
519                            {
520                                this.assertMessagesUniqueness( i.getMessages(), details );
521                            }
522    
523                            if ( i.getProperties() != null )
524                            {
525                                this.assertPropertiesUniqueness( i.getProperties(), details );
526                            }
527    
528                            if ( i.getSpecifications() != null && !i.getSpecifications().getSpecification().isEmpty() )
529                            {
530                                for ( Specification s : i.getSpecifications().getSpecification() )
531                                {
532                                    details.add( this.newImplementationSpecificationDeclarationConstraintDetail(
533                                        this.getObjectFactory().createImplementation( i ), i, s ) );
534    
535                                }
536                            }
537    
538                            final Specifications specs = modules.getSpecifications( i.getIdentifier() );
539                            final Dependencies deps = modules.getDependencies( i.getIdentifier() );
540    
541                            if ( specs != null )
542                            {
543                                for ( SpecificationReference r : specs.getReference() )
544                                {
545                                    final Specification s = specs.getSpecification( r.getIdentifier() );
546    
547                                    if ( s != null && r.getVersion() != null && s.getVersion() != null &&
548                                         VersionParser.compare( r.getVersion(), s.getVersion() ) != 0 )
549                                    {
550                                        final Module moduleOfSpecification =
551                                            modules.getModuleOfSpecification( s.getIdentifier() );
552    
553                                        details.add( this.newIncompatibleImplementationDetail(
554                                            this.getObjectFactory().createImplementation( i ),
555                                            i.getIdentifier(), m.getName(),
556                                            r.getIdentifier(), moduleOfSpecification == null
557                                                               ? "<>" : moduleOfSpecification.getName(),
558                                            r.getVersion(), s.getVersion() ) );
559    
560                                    }
561                                }
562                            }
563    
564                            if ( deps != null )
565                            {
566                                for ( Dependency d : deps.getDependency() )
567                                {
568                                    final Specification s = modules.getSpecification( d.getIdentifier() );
569    
570                                    if ( s != null )
571                                    {
572                                        if ( s.getVersion() != null && d.getVersion() != null &&
573                                             VersionParser.compare( d.getVersion(), s.getVersion() ) > 0 )
574                                        {
575                                            final Module moduleOfSpecification =
576                                                modules.getModuleOfSpecification( s.getIdentifier() );
577    
578                                            details.add( this.newIncompatibleDependencyDetail(
579                                                this.getObjectFactory().createDependency( d ),
580                                                i.getIdentifier(), m.getName(),
581                                                d.getIdentifier(), moduleOfSpecification == null
582                                                                   ? "<>" : moduleOfSpecification.getName(),
583                                                d.getVersion(), s.getVersion() ) );
584    
585                                        }
586    
587                                        if ( d.getProperties() != null )
588                                        {
589                                            this.assertPropertiesUniqueness( d.getProperties(), details );
590    
591                                            if ( !d.getProperties().getReference().isEmpty() )
592                                            {
593                                                details.add( this.newDependencyPropertyReferenceConstraintDetail(
594                                                    this.getObjectFactory().createDependency( d ), i, d ) );
595    
596                                            }
597    
598                                            if ( s.getScope() != null && !d.getProperties().getProperty().isEmpty() )
599                                            {
600                                                details.add( this.newPropertyOverwriteConstraintDetail(
601                                                    this.getObjectFactory().createDependency( d ), i, d, s,
602                                                    s.getScope() ) );
603    
604                                            }
605                                        }
606                                    }
607    
608                                    final Implementations available = modules.getImplementations( d.getIdentifier() );
609    
610                                    if ( !d.isOptional() )
611                                    {
612                                        boolean missing = false;
613    
614                                        if ( available == null )
615                                        {
616                                            missing = true;
617                                        }
618                                        else if ( available.getImplementation().isEmpty() )
619                                        {
620                                            missing = true;
621                                        }
622                                        else if ( d.getImplementationName() != null &&
623                                                  available.getImplementationByName( d.getImplementationName() ) == null )
624                                        {
625                                            missing = true;
626                                        }
627    
628                                        if ( missing )
629                                        {
630                                            details.add( this.newMandatoryDependencyConstraintDetail(
631                                                this.getObjectFactory().createDependency( d ), i.getIdentifier(),
632                                                d.getName() ) );
633    
634                                        }
635                                    }
636                                }
637                            }
638    
639                            if ( i.getParent() != null )
640                            {
641                                final Implementation parent = modules.getImplementation( i.getParent() );
642                                if ( parent != null && parent.isFinal() )
643                                {
644                                    details.add( this.newInheritanceConstraintDetail(
645                                        this.getObjectFactory().createImplementation( i ), i, parent ) );
646    
647                                }
648                            }
649                        }
650                    }
651    
652                    if ( m.getSpecifications() != null )
653                    {
654                        for ( SpecificationReference r : m.getSpecifications().getReference() )
655                        {
656                            details.add( this.newModuleSpecificationReferenceConstraintDetail(
657                                this.getObjectFactory().createModule( m ), m, r ) );
658    
659                        }
660    
661                        for ( Specification s : m.getSpecifications().getSpecification() )
662                        {
663                            if ( s.getProperties() != null )
664                            {
665                                this.assertPropertiesUniqueness( s.getProperties(), details );
666                            }
667    
668                            final Implementations impls = modules.getImplementations( s.getIdentifier() );
669    
670                            if ( impls != null )
671                            {
672                                final Map<String, Implementation> map = new HashMap<String, Implementation>();
673    
674                                for ( Implementation i : impls.getImplementation() )
675                                {
676                                    if ( map.containsKey( i.getName() ) )
677                                    {
678                                        details.add( this.newImplementationNameConstraintDetail(
679                                            this.getObjectFactory().createSpecification( s ), s.getIdentifier(),
680                                            i.getIdentifier() + ", " + map.get( i.getName() ).getIdentifier(),
681                                            i.getName() ) );
682    
683                                    }
684    
685                                    map.put( i.getName(), i );
686                                }
687    
688                                if ( s.getMultiplicity() == Multiplicity.ONE && impls.getImplementation().size() > 1 )
689                                {
690                                    details.add( this.newMultiplicityConstraintDetail(
691                                        this.getObjectFactory().createSpecification( s ), impls.getImplementation().size(),
692                                        s.getIdentifier(), 1, s.getMultiplicity() ) );
693    
694                                }
695                            }
696                        }
697                    }
698                }
699    
700                if ( !details.isEmpty() )
701                {
702                    final ModelException modelException = new ModelException( this.getMessage( "validationFailed", null ) );
703                    modelException.getDetails().addAll( details );
704                    throw modelException;
705                }
706            }
707            catch ( final TokenMgrError e )
708            {
709                throw new ModelException( e.getMessage(), e );
710            }
711            catch ( final ParseException e )
712            {
713                throw new ModelException( e.getMessage(), e );
714            }
715        }
716    
717        public <T extends ModelObject> T transformModelObject(
718            final JAXBElement<T> modelObject, final Transformer transformer )
719            throws IOException, SAXException, JAXBException, TransformerException
720        {
721            if ( modelObject == null )
722            {
723                throw new NullPointerException( "modelObject" );
724            }
725            if ( transformer == null )
726            {
727                throw new NullPointerException( "transformer" );
728            }
729    
730            final JAXBContext ctx = this.getContext();
731            final JAXBSource source = new JAXBSource( ctx, modelObject );
732            final JAXBResult result = new JAXBResult( ctx );
733            transformer.transform( source, result );
734            return ( (JAXBElement<T>) result.getResult() ).getValue();
735        }
736    
737        public Instance getInstance( final Modules modules, final Implementation implementation, final ClassLoader cl )
738        {
739            if ( modules == null )
740            {
741                throw new NullPointerException( "modules" );
742            }
743            if ( implementation == null )
744            {
745                throw new NullPointerException( "implementation" );
746            }
747            if ( cl == null )
748            {
749                throw new NullPointerException( "classLoader" );
750            }
751    
752            final Instance instance = new Instance();
753            instance.setIdentifier( implementation.getIdentifier() );
754            instance.setImplementationName( implementation.getName() );
755            instance.setClazz( implementation.getClazz() );
756            instance.setClassLoader( cl );
757            instance.setStateless( implementation.isStateless() );
758            instance.setDependencies( modules.getDependencies( implementation.getIdentifier() ) );
759            instance.setProperties( modules.getProperties( implementation.getIdentifier() ) );
760            instance.setMessages( modules.getMessages( implementation.getIdentifier() ) );
761            instance.setSpecifications( modules.getSpecifications( implementation.getIdentifier() ) );
762            return instance;
763        }
764    
765        public Instance getInstance( final Modules modules, final Implementation implementation,
766                                     final Dependency dependency, final ClassLoader cl )
767        {
768            if ( modules == null )
769            {
770                throw new NullPointerException( "modules" );
771            }
772            if ( implementation == null )
773            {
774                throw new NullPointerException( "implementation" );
775            }
776            if ( dependency == null )
777            {
778                throw new NullPointerException( "dependency" );
779            }
780            if ( cl == null )
781            {
782                throw new NullPointerException( "cl" );
783            }
784    
785            final Instance instance = this.getInstance( modules, implementation, cl );
786            final Specification dependencySpecification = modules.getSpecification( dependency.getIdentifier() );
787    
788            if ( dependencySpecification != null && dependencySpecification.getScope() == null &&
789                 dependency.getProperties() != null && !dependency.getProperties().getProperty().isEmpty() )
790            {
791                final Properties properties = new Properties();
792                properties.getProperty().addAll( dependency.getProperties().getProperty() );
793    
794                if ( instance.getProperties() != null )
795                {
796                    for ( Property p : instance.getProperties().getProperty() )
797                    {
798                        if ( properties.getProperty( p.getName() ) == null )
799                        {
800                            properties.getProperty().add( p );
801                        }
802                    }
803                }
804    
805                instance.setProperties( properties );
806            }
807    
808            return instance;
809        }
810    
811        public Instance getInstance( final Modules modules, final Object object )
812        {
813            if ( modules == null )
814            {
815                throw new NullPointerException( "modules" );
816            }
817            if ( object == null )
818            {
819                throw new NullPointerException( "object" );
820            }
821    
822            synchronized ( this.objects )
823            {
824                Instance instance = (Instance) this.objects.get( object );
825    
826                if ( instance == null )
827                {
828                    final Implementation i = this.getImplementation( modules, object );
829    
830                    if ( i != null )
831                    {
832                        ClassLoader cl = object.getClass().getClassLoader();
833                        if ( cl == null )
834                        {
835                            cl = ClassLoader.getSystemClassLoader();
836                        }
837    
838                        instance = this.getInstance( modules, i, cl );
839                        if ( instance != null )
840                        {
841                            this.objects.put( object, instance );
842                        }
843                    }
844                }
845    
846                return instance;
847            }
848        }
849    
850        public Object getObject( final Modules modules, final Specification specification, final Instance instance )
851            throws InstantiationException
852        {
853            if ( modules == null )
854            {
855                throw new NullPointerException( "modules" );
856            }
857            if ( specification == null )
858            {
859                throw new NullPointerException( "specification" );
860            }
861            if ( instance == null )
862            {
863                throw new NullPointerException( "instance" );
864            }
865    
866            Object object = null;
867    
868            try
869            {
870                final Class specClass = Class.forName( specification.getClazz(), true, instance.getClassLoader() );
871                final Class clazz = Class.forName( instance.getClazz(), true, instance.getClassLoader() );
872    
873                if ( Modifier.isPublic( clazz.getModifiers() ) )
874                {
875                    Constructor ctor = null;
876    
877                    try
878                    {
879                        ctor = clazz.getConstructor( NO_CLASSES );
880                    }
881                    catch ( final NoSuchMethodException e )
882                    {
883                        this.log( Level.FINE, this.getMessage( "noSuchMethod", new Object[]
884                            {
885                                e.getMessage()
886                            } ), null );
887    
888                        ctor = null;
889                    }
890    
891                    if ( ctor != null && specClass.isAssignableFrom( clazz ) )
892                    {
893                        synchronized ( this.objects )
894                        {
895                            object = clazz.newInstance();
896                            this.objects.put( object, instance );
897                        }
898                    }
899                    else
900                    {
901                        final StringBuilder methodNames = new StringBuilder().append( '[' );
902                        Method factoryMethod = null;
903                        String methodName = null;
904    
905                        char[] c = instance.getImplementationName().toCharArray();
906                        c[0] = Character.toUpperCase( c[0] );
907                        methodName = "get" + String.valueOf( c );
908    
909                        boolean javaIdentifier = Character.isJavaIdentifierStart( c[0] );
910                        if ( javaIdentifier )
911                        {
912                            for ( int idx = c.length - 1; idx > 0; idx-- )
913                            {
914                                if ( !Character.isJavaIdentifierPart( c[idx] ) )
915                                {
916                                    javaIdentifier = false;
917                                    break;
918                                }
919                            }
920                        }
921    
922                        if ( javaIdentifier )
923                        {
924                            methodNames.append( methodName );
925                            factoryMethod = this.getFactoryMethod( clazz, methodName );
926                        }
927    
928                        if ( factoryMethod == null )
929                        {
930                            methodName = specification.getIdentifier().substring(
931                                specification.getIdentifier().lastIndexOf( '.' ) + 1 );
932    
933                            c = methodName.toCharArray();
934                            c[0] = Character.toUpperCase( c[0] );
935    
936                            javaIdentifier = Character.isJavaIdentifierStart( c[0] );
937                            if ( javaIdentifier )
938                            {
939                                for ( int idx = c.length - 1; idx > 0; idx-- )
940                                {
941                                    if ( !Character.isJavaIdentifierPart( c[idx] ) )
942                                    {
943                                        javaIdentifier = false;
944                                        break;
945                                    }
946                                }
947                            }
948    
949                            if ( javaIdentifier )
950                            {
951                                methodName = "get" + String.valueOf( c );
952                                methodNames.append( " " ).append( methodName );
953                                factoryMethod = this.getFactoryMethod( clazz, methodName );
954                            }
955                        }
956    
957                        if ( factoryMethod == null )
958                        {
959                            methodName = "getObject";
960                            methodNames.append( " " ).append( methodName );
961                            factoryMethod = this.getFactoryMethod( clazz, methodName );
962                        }
963    
964                        methodNames.append( ']' );
965    
966                        if ( factoryMethod == null )
967                        {
968                            throw new InstantiationException( this.getMessage( "missingFactoryMethod", new Object[]
969                                {
970                                    clazz.getName(), instance.getIdentifier(), methodNames.toString()
971                                } ) );
972    
973                        }
974    
975                        if ( Modifier.isStatic( factoryMethod.getModifiers() ) )
976                        {
977                            object = factoryMethod.invoke( null, NO_OBJECTS );
978                        }
979                        else if ( ctor != null )
980                        {
981                            synchronized ( this.objects )
982                            {
983                                object = ctor.newInstance();
984                                this.objects.put( object, instance );
985                                object = factoryMethod.invoke( object, NO_OBJECTS );
986                                this.objects.put( object, instance );
987                            }
988                        }
989                        else
990                        {
991                            throw new InstantiationException( this.getMessage( "missingFactoryMethod", new Object[]
992                                {
993                                    clazz.getName(), instance.getIdentifier(), methodNames.toString()
994                                } ) );
995    
996                        }
997                    }
998                }
999    
1000                return object;
1001            }
1002            catch ( final InvocationTargetException e )
1003            {
1004                throw (InstantiationException) new InstantiationException().initCause(
1005                    e.getTargetException() != null ? e.getTargetException() : e );
1006    
1007            }
1008            catch ( final IllegalAccessException e )
1009            {
1010                throw (InstantiationException) new InstantiationException().initCause( e );
1011            }
1012            catch ( final ClassNotFoundException e )
1013            {
1014                throw (InstantiationException) new InstantiationException().initCause( e );
1015            }
1016        }
1017    
1018        // SECTION-END
1019        // SECTION-START[DefaultModelManager]
1020        /** Listener interface. */
1021        public interface Listener
1022        {
1023    
1024            /**
1025             * Get called on logging.
1026             *
1027             * @param level The level of the event.
1028             * @param message The message of the event or {@code null}.
1029             * @param t The throwable of the event or {@code null}.
1030             */
1031            void onLog( Level level, String message, Throwable t );
1032    
1033        }
1034    
1035        /**
1036         * Constant for the name of the classpath module.
1037         * @see #getClasspathModuleName()
1038         */
1039        private static final String DEFAULT_CLASSPATH_MODULE_NAME = "Java Classpath";
1040    
1041        /**
1042         * Classpath location searched for documents by default.
1043         * @see #getDefaultDocumentLocation()
1044         */
1045        private static final String DEFAULT_DOCUMENT_LOCATION = "META-INF/jomc.xml";
1046    
1047        /**
1048         * Classpath location searched for style sheets by default.
1049         * @see #getDefaultStylesheetLocation()
1050         */
1051        private static final String DEFAULT_STYLESHEET_LOCATION = "META-INF/jomc.xslt";
1052    
1053        /** Classpath location of the bootstrap schema. */
1054        private static final String BOOTSTRAP_SCHEMA_LOCATION =
1055            Schemas.class.getPackage().getName().replace( '.', '/' ) + "/jomc-bootstrap-1.0.xsd";
1056    
1057        /**
1058         * Classpath location searched for bootstrap documents by default.
1059         * @see #getBootstrapDocumentLocation()
1060         */
1061        private static final String DEFAULT_BOOTSTRAP_DOCUMENT_LOCATION = "META-INF/jomc-bootstrap.xml";
1062    
1063        /** JAXB context of the bootstrap schema. */
1064        private static final String BOOTSTRAP_CONTEXT = Schemas.class.getPackage().getName();
1065    
1066        /** Supported schema name extensions. */
1067        private static final String[] SCHEMA_EXTENSIONS = new String[]
1068        {
1069            "xsd"
1070        };
1071    
1072        /** Empty {@code Class} array. */
1073        private static final Class[] NO_CLASSES =
1074        {
1075        };
1076    
1077        /** Empty {@code Object} array. */
1078        private static final Object[] NO_OBJECTS =
1079        {
1080        };
1081    
1082        /** Class loader of the instance. */
1083        private ClassLoader classLoader;
1084    
1085        /** The entity resolver of the instance. */
1086        private EntityResolver entityResolver;
1087    
1088        /** The L/S resolver of the instance. */
1089        private LSResourceResolver resourceResolver;
1090    
1091        /** The context of the instance. */
1092        private JAXBContext context;
1093    
1094        /** The schema of the instance. */
1095        private javax.xml.validation.Schema schema;
1096    
1097        /** The bootstrap schema. */
1098        private javax.xml.validation.Schema bootstrapSchema;
1099    
1100        /** URLs of all available classpath schema resources. */
1101        private Set<URL> schemaResources;
1102    
1103        /** Schemas of the instance. */
1104        private Schemas schemas;
1105    
1106        /** Object factory of the instance. */
1107        private ObjectFactory objectFactory;
1108    
1109        /** Bootstrap object factory of the instance. */
1110        private org.jomc.model.bootstrap.ObjectFactory bootstrapObjectFactory;
1111    
1112        /** The listeners of the instance. */
1113        private List<Listener> listeners;
1114    
1115        /** Maps objects to {@code Instance}s. */
1116        private final Map objects = new WeakIdentityHashMap( 1024 );
1117    
1118        /** Creates a new {@code DefaultModelManager} instance. */
1119        public DefaultModelManager()
1120        {
1121            super();
1122        }
1123    
1124        /**
1125         * Gets the bootstrap object factory of the instance.
1126         *
1127         * @return The bootstrap object factory of the instance.
1128         */
1129        public org.jomc.model.bootstrap.ObjectFactory getBootstrapObjectFactory()
1130        {
1131            if ( this.bootstrapObjectFactory == null )
1132            {
1133                this.bootstrapObjectFactory = new org.jomc.model.bootstrap.ObjectFactory();
1134            }
1135    
1136            return this.bootstrapObjectFactory;
1137        }
1138    
1139        /**
1140         * Gets a new bootstrap context instance.
1141         *
1142         * @return A new bootstrap context instance.
1143         *
1144         * @throws JAXBException if creating a new bootstrap context instance fails.
1145         */
1146        public JAXBContext getBootstrapContext() throws JAXBException
1147        {
1148            return JAXBContext.newInstance( BOOTSTRAP_CONTEXT, this.getClassLoader() );
1149        }
1150    
1151        /**
1152         * Gets a new bootstrap {@code Marshaller}.
1153         *
1154         * @param validating {@code true} for a marshaller with additional schema validation support enabled; {@code false}
1155         * for a marshaller without additional schema validation support enabled.
1156         * @param formattedOutput {@code true} for the marshaller to produce formatted output; {@code false} for the
1157         * marshaller to not apply any formatting when marshalling.
1158         *
1159         * @return A new bootstrap {@code Marshaller}.
1160         *
1161         * @throws IOException if reading schema resources fails.
1162         * @throws SAXException if parsing schema resources fails.
1163         * @throws JAXBException if unmarshalling schema resources fails.
1164         */
1165        public Marshaller getBootstrapMarshaller( final boolean validating, final boolean formattedOutput )
1166            throws IOException, SAXException, JAXBException
1167        {
1168            final Marshaller m = this.getBootstrapContext().createMarshaller();
1169            m.setProperty( Marshaller.JAXB_ENCODING, "UTF-8" );
1170            m.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.valueOf( formattedOutput ) );
1171    
1172            if ( validating )
1173            {
1174                m.setSchema( this.getBootstrapSchema() );
1175            }
1176    
1177            return m;
1178        }
1179    
1180        /**
1181         * Gets a new bootstrap {@code Unmarshaller}.
1182         *
1183         * @param validating {@code true} for an unmarshaller with additional schema validation support enabled;
1184         * {@code false} for an unmarshaller without additional schema validation support enabled.
1185         *
1186         * @return A new bootstrap {@code Unmarshaller}.
1187         *
1188         * @throws IOException if reading schema resources fails.
1189         * @throws SAXException if parsing schema resources fails.
1190         * @throws JAXBException if unmarshalling schema resources fails.
1191         */
1192        public Unmarshaller getBootstrapUnmarshaller( final boolean validating )
1193            throws IOException, SAXException, JAXBException
1194        {
1195            final Unmarshaller u = this.getBootstrapContext().createUnmarshaller();
1196            if ( validating )
1197            {
1198                u.setSchema( this.getBootstrapSchema() );
1199            }
1200    
1201            return u;
1202        }
1203    
1204        /**
1205         * Gets the bootstrap schema.
1206         *
1207         * @return The bootstrap schema.
1208         *
1209         * @throws SAXException if parsing the bootstrap schema fails.
1210         */
1211        public javax.xml.validation.Schema getBootstrapSchema() throws SAXException
1212        {
1213            if ( this.bootstrapSchema == null )
1214            {
1215                final URL url = this.getClassLoader().getResource( BOOTSTRAP_SCHEMA_LOCATION );
1216                this.log( Level.FINE, this.getMessage( "processing", new Object[]
1217                    {
1218                        url.toExternalForm()
1219                    } ), null );
1220    
1221                this.bootstrapSchema = SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI ).newSchema( url );
1222            }
1223    
1224            return this.bootstrapSchema;
1225        }
1226    
1227        /**
1228         * Validates a given bootstrap object.
1229         *
1230         * @param bootstrapObject The object to validate.
1231         *
1232         * @throws NullPointerException if {@code bootstrapObject} is {@code null}.
1233         * @throws ModelException if {@code bootstrapObject} is invalid.
1234         * @throws IOException if reading schema resources fails.
1235         * @throws SAXException if parsing schema resources fails.
1236         * @throws JAXBException if unmarshalling schema resources fails.
1237         */
1238        public void validateBootstrapObject( final JAXBElement<? extends BootstrapObject> bootstrapObject )
1239            throws ModelException, IOException, SAXException, JAXBException
1240        {
1241            if ( bootstrapObject == null )
1242            {
1243                throw new NullPointerException( "bootstrapObject" );
1244            }
1245    
1246            final StringWriter stringWriter = new StringWriter();
1247            final Validator validator = this.getBootstrapSchema().newValidator();
1248            final ModelExceptionErrorHandler errorHandler = new ModelExceptionErrorHandler();
1249            validator.setErrorHandler( errorHandler );
1250            this.getBootstrapMarshaller( false, false ).marshal( bootstrapObject, stringWriter );
1251    
1252            try
1253            {
1254                validator.validate( new StreamSource( new StringReader( stringWriter.toString() ) ) );
1255            }
1256            catch ( final SAXException e )
1257            {
1258                final ModelException modelException = new ModelException( e.getMessage(), e );
1259                modelException.getDetails().addAll( errorHandler.getDetails() );
1260                throw modelException;
1261            }
1262        }
1263    
1264        /**
1265         * Transforms a given {@code BootstrapObject} with a given {@code Transformer}.
1266         *
1267         * @param bootstrapObject The {@code BootstrapObject} to transform.
1268         * @param transformer The {@code Transformer} to transform {@code bootstrapObject} with.
1269         * @param <T> The type of {@code bootstrapObject}.
1270         *
1271         * @return {@code bootstrapObject} transformed with {@code transformer}.
1272         *
1273         * @throws NullPointerException if {@code bootstrapObject} or {@code transformer} is {@code null}.
1274         * @throws IOException if reading schema resources fails.
1275         * @throws SAXException if parsing schema resources fails.
1276         * @throws JAXBException if binding fails.
1277         * @throws TransformerException if the transformation fails.
1278         */
1279        public <T extends BootstrapObject> T transformBootstrapObject(
1280            final JAXBElement<T> bootstrapObject, final Transformer transformer )
1281            throws IOException, SAXException, JAXBException, TransformerException
1282        {
1283            if ( bootstrapObject == null )
1284            {
1285                throw new NullPointerException( "bootstrapObject" );
1286            }
1287            if ( transformer == null )
1288            {
1289                throw new NullPointerException( "transformer" );
1290            }
1291    
1292            final JAXBContext ctx = this.getBootstrapContext();
1293            final JAXBSource source = new JAXBSource( ctx, bootstrapObject );
1294            final JAXBResult result = new JAXBResult( ctx );
1295            transformer.transform( source, result );
1296            return ( (JAXBElement<T>) result.getResult() ).getValue();
1297        }
1298    
1299        /**
1300         * Sets the object factory of the instance.
1301         *
1302         * @param value The new object factory of the instance or {@code null}.
1303         *
1304         * @see #getObjectFactory()
1305         */
1306        public void setObjectFactory( final ObjectFactory value )
1307        {
1308            this.objectFactory = value;
1309        }
1310    
1311        /**
1312         * Sets the entity resolver of the instance.
1313         *
1314         * @param value The new entity resolver of the instance or {@code null}.
1315         *
1316         * @see #getEntityResolver()
1317         */
1318        public void setEntityResolver( final EntityResolver value )
1319        {
1320            this.entityResolver = value;
1321        }
1322    
1323        /**
1324         * Sets the L/S resolver of the instance.
1325         *
1326         * @param value The new L/S resolver of the instance or {@code null}.
1327         *
1328         * @see #getLSResourceResolver()
1329         */
1330        public void setLSResourceResolver( final LSResourceResolver value )
1331        {
1332            this.resourceResolver = value;
1333        }
1334    
1335        /**
1336         * Sets the JAXB context of the instance.
1337         *
1338         * @param value The new JAXB context of the instance or {@code null}.
1339         *
1340         * @see #getContext()
1341         */
1342        public void setContext( final JAXBContext value )
1343        {
1344            this.context = value;
1345        }
1346    
1347        /**
1348         * Sets the schema of the instance.
1349         *
1350         * @param value The new schema of the instance or {@code null}.
1351         *
1352         * @see #getSchema()
1353         */
1354        public void setSchema( final javax.xml.validation.Schema value )
1355        {
1356            this.schema = value;
1357        }
1358    
1359        /**
1360         * Gets the list of registered listeners.
1361         *
1362         * @return The list of registered listeners.
1363         *
1364         * @see #log(java.util.logging.Level, java.lang.String, java.lang.Throwable)
1365         */
1366        public List<Listener> getListeners()
1367        {
1368            if ( this.listeners == null )
1369            {
1370                this.listeners = new LinkedList<Listener>();
1371            }
1372    
1373            return this.listeners;
1374        }
1375    
1376        /**
1377         * Gets the location to search for bootstrap documents.
1378         * <p>The bootstrap document location is controlled by system property
1379         * {@code org.jomc.model.DefaultModelManager.bootstrapDocumentLocation} holding the location to search at. If that
1380         * property is not set, the {@code META-INF/jomc-bootstrap.xml} default is returned.</p>
1381         *
1382         * @return The location to search for bootstrap documents.
1383         *
1384         * @see #getSchemas()
1385         */
1386        public String getBootstrapDocumentLocation()
1387        {
1388            return System.getProperty( "org.jomc.model.DefaultModelManager.bootstrapDocumentLocation",
1389                                       DEFAULT_BOOTSTRAP_DOCUMENT_LOCATION );
1390    
1391        }
1392    
1393        /**
1394         * Gets the schemas backing the instance.
1395         *
1396         * @return The schemas backing the instance.
1397         *
1398         * @throws IOException if reading schema resources fails.
1399         * @throws SAXException if parsing schema resources fails.
1400         * @throws JAXBException if unmarshalling schema resources fails.
1401         *
1402         * @see #getBootstrapDocumentLocation()
1403         */
1404        public Schemas getSchemas() throws IOException, JAXBException, SAXException
1405        {
1406            if ( this.schemas == null )
1407            {
1408                this.schemas = new Schemas();
1409    
1410                final JAXBContext ctx = JAXBContext.newInstance( BOOTSTRAP_CONTEXT, this.getClassLoader() );
1411                final Unmarshaller u = ctx.createUnmarshaller();
1412                final String bootstrapLocation = this.getBootstrapDocumentLocation();
1413                this.log( Level.FINE, this.getMessage( "bootstrapLocation", new Object[]
1414                    {
1415                        bootstrapLocation
1416                    } ), null );
1417    
1418                final Enumeration<URL> e = this.getClassLoader().getResources( bootstrapLocation );
1419                u.setSchema( this.getBootstrapSchema() );
1420    
1421                while ( e.hasMoreElements() )
1422                {
1423                    final URL url = e.nextElement();
1424                    this.log( Level.FINE, this.getMessage( "processing", new Object[]
1425                        {
1426                            url.toExternalForm()
1427                        } ), null );
1428    
1429                    Object content = u.unmarshal( url );
1430                    if ( content instanceof JAXBElement )
1431                    {
1432                        content = ( (JAXBElement) content ).getValue();
1433                    }
1434    
1435                    if ( content instanceof Schema )
1436                    {
1437                        final Schema s = (Schema) content;
1438                        this.log( Level.FINE, this.getMessage( "addingSchema", new Object[]
1439                            {
1440                                s.getPublicId(), s.getSystemId(), s.getContextId(), s.getClasspathId()
1441                            } ), null );
1442    
1443                        this.schemas.getSchema().add( s );
1444                    }
1445                    else if ( content instanceof Schemas )
1446                    {
1447                        for ( Schema s : ( (Schemas) content ).getSchema() )
1448                        {
1449                            this.log( Level.FINE, this.getMessage( "addingSchema", new Object[]
1450                                {
1451                                    s.getPublicId(), s.getSystemId(), s.getContextId(), s.getClasspathId()
1452                                } ), null );
1453    
1454                            this.schemas.getSchema().add( s );
1455                        }
1456                    }
1457                }
1458            }
1459    
1460            return this.schemas;
1461        }
1462    
1463        /**
1464         * Gets the default location to search for documents.
1465         * <p>The default document location is controlled by system property
1466         * {@code org.jomc.model.DefaultModelManager.defaultDocumentLocation} holding the location to search at by default.
1467         * If that property is not set, the {@code META-INF/jomc.xml} default is returned.</p>
1468         *
1469         * @return The default location to search for documents.
1470         *
1471         * @see #getClasspathModules(java.lang.String)
1472         */
1473        public String getDefaultDocumentLocation()
1474        {
1475            return System.getProperty( "org.jomc.model.DefaultModelManager.defaultDocumentLocation",
1476                                       DEFAULT_DOCUMENT_LOCATION );
1477    
1478        }
1479    
1480        /**
1481         * Gets modules by searching the class loader of the instance for resources.
1482         * <p><b>Note:</b><br/>
1483         * This method does not validate the modules.</p>
1484         *
1485         * @param location The location to search at.
1486         *
1487         * @return All resources from the class loader of the instance matching {@code location}.
1488         *
1489         * @throws NullPointerException if {@code location} is {@code null}.
1490         * @throws IOException if reading resources fails.
1491         * @throws SAXException if parsing schema resources fails.
1492         * @throws JAXBException if unmarshalling schema resources fails.
1493         *
1494         * @see #getDefaultDocumentLocation()
1495         */
1496        public Modules getClasspathModules( final String location ) throws IOException, SAXException, JAXBException
1497        {
1498            if ( location == null )
1499            {
1500                throw new NullPointerException( "location" );
1501            }
1502    
1503            this.log( Level.FINE, this.getMessage( "documentLocation", new Object[]
1504                {
1505                    location
1506                } ), null );
1507    
1508            final long t0 = System.currentTimeMillis();
1509            final Text text = new Text();
1510            text.setLanguage( "en" );
1511            text.setValue( this.getMessage( "classpathModulesInfo", new Object[]
1512                {
1513                    location
1514                } ) );
1515    
1516            final Modules mods = new Modules();
1517            mods.setDocumentation( new Texts() );
1518            mods.getDocumentation().setDefaultLanguage( "en" );
1519            mods.getDocumentation().getText().add( text );
1520    
1521            final Unmarshaller u = this.getUnmarshaller( false );
1522            final Enumeration<URL> resources = this.getClassLoader().getResources( location );
1523    
1524            Integer count = 0;
1525            while ( resources.hasMoreElements() )
1526            {
1527                count++;
1528                final URL url = resources.nextElement();
1529    
1530                this.log( Level.FINE, this.getMessage( "processing", new Object[]
1531                    {
1532                        url.toExternalForm()
1533                    } ), null );
1534    
1535                Object content = u.unmarshal( url );
1536                if ( content instanceof JAXBElement )
1537                {
1538                    content = ( (JAXBElement) content ).getValue();
1539                }
1540    
1541                if ( content instanceof Module )
1542                {
1543                    mods.getModule().add( (Module) content );
1544                }
1545                else
1546                {
1547                    this.log( Level.WARNING, this.getMessage( "ignoringDocument", new Object[]
1548                        {
1549                            content == null ? "<>" : content.toString(), url.toExternalForm()
1550                        } ), null );
1551    
1552                }
1553            }
1554    
1555            this.log( Level.FINE, this.getMessage( "classpathReport", new Object[]
1556                {
1557                    count, Long.valueOf( System.currentTimeMillis() - t0 )
1558                } ), null );
1559    
1560            return mods;
1561        }
1562    
1563        /**
1564         * Gets the classpath module name.
1565         * <p>The classpath module name is controlled by system property
1566         * {@code org.jomc.model.DefaultModelManager.classpathModuleName} holding the classpath module name.
1567         * If that property is not set, the {@code Java Classpath} default is returned.</p>
1568         *
1569         * @return The name of the classpath module.
1570         *
1571         * @see #getClasspathModule(org.jomc.model.Modules)
1572         */
1573        public String getClasspathModuleName()
1574        {
1575            return System.getProperty( "org.jomc.model.DefaultModelManager.classpathModuleName",
1576                                       DEFAULT_CLASSPATH_MODULE_NAME );
1577    
1578        }
1579    
1580        /**
1581         * Gets a module holding model objects resolved by inspecting the class loader of the instance.
1582         * <p>This method searches the given modules for unresolved references and tries to resolve each unresolved
1583         * reference by inspecting the class loader of the instance.</p>
1584         *
1585         * @param modules The modules to resolve by inspecting the class loader of the instance.
1586         *
1587         * @return A module holding model objects resolved by inspecting the class loader of the instance or {@code null} if
1588         * nothing could be resolved.
1589         *
1590         * @see #getClasspathModuleName()
1591         */
1592        public Module getClasspathModule( final Modules modules )
1593        {
1594            final Module module = new Module();
1595            module.setVersion( System.getProperty( "java.specification.version" ) );
1596            module.setName( this.getClasspathModuleName() );
1597    
1598            this.resolveClasspath( modules, module );
1599    
1600            final boolean resolved = ( module.getSpecifications() != null &&
1601                                       !module.getSpecifications().getSpecification().isEmpty() ) ||
1602                                     ( module.getImplementations() != null &&
1603                                       !module.getImplementations().getImplementation().isEmpty() );
1604    
1605            return resolved ? module : null;
1606        }
1607    
1608        /**
1609         * Gets the default location to search for style sheets.
1610         * <p>The default style sheet location is controlled by system property
1611         * {@code org.jomc.model.DefaultModelManager.defaultStylesheetLocation} holding the location to search at by
1612         * default. If that property is not set, the {@code META-INF/jomc.xslt} default is returned.</p>
1613         *
1614         * @return The default location to search for style sheets.
1615         *
1616         * @see #getClasspathTransformers(java.lang.String)
1617         */
1618        public String getDefaultStylesheetLocation()
1619        {
1620            return System.getProperty( "org.jomc.model.DefaultModelManager.defaultStylesheetLocation",
1621                                       DEFAULT_STYLESHEET_LOCATION );
1622    
1623        }
1624    
1625        /**
1626         * Gets transformers by searching the class loader of the instance for resources.
1627         *
1628         * @param location The location to search at.
1629         *
1630         * @return All resources from the class loader of the instance matching {@code location}.
1631         *
1632         * @throws NullPointerException if {@code location} is {@code null}.
1633         * @throws IOException if reading resources fails.
1634         * @throws TransformerConfigurationException if getting the transformers fails.
1635         *
1636         * @see #getDefaultStylesheetLocation()
1637         */
1638        public List<Transformer> getClasspathTransformers( final String location )
1639            throws IOException, TransformerConfigurationException
1640        {
1641            if ( location == null )
1642            {
1643                throw new NullPointerException( "location" );
1644            }
1645    
1646            this.log( Level.FINE, this.getMessage( "stylesheetLocation", new Object[]
1647                {
1648                    location
1649                } ), null );
1650    
1651            final long t0 = System.currentTimeMillis();
1652            final List<Transformer> transformers = new LinkedList<Transformer>();
1653            final TransformerFactory transformerFactory = TransformerFactory.newInstance();
1654            final Enumeration<URL> resources = this.getClassLoader().getResources( location );
1655            final ErrorListener errorListener = new ErrorListener()
1656            {
1657    
1658                public void warning( final TransformerException exception ) throws TransformerException
1659                {
1660                    log( Level.WARNING, exception.getMessage(), exception );
1661                }
1662    
1663                public void error( final TransformerException exception ) throws TransformerException
1664                {
1665                    log( Level.SEVERE, exception.getMessage(), exception );
1666                    throw exception;
1667                }
1668    
1669                public void fatalError( final TransformerException exception ) throws TransformerException
1670                {
1671                    log( Level.SEVERE, exception.getMessage(), exception );
1672                    throw exception;
1673                }
1674    
1675            };
1676    
1677            final URIResolver uriResolver = new URIResolver()
1678            {
1679    
1680                public Source resolve( final String href, final String base ) throws TransformerException
1681                {
1682                    try
1683                    {
1684                        Source source = null;
1685                        final InputSource inputSource = getEntityResolver().resolveEntity( null, href );
1686    
1687                        if ( inputSource != null )
1688                        {
1689                            source = new SAXSource( inputSource );
1690                        }
1691    
1692                        return source;
1693                    }
1694                    catch ( final SAXException e )
1695                    {
1696                        log( Level.SEVERE, e.getMessage(), e );
1697                        throw new TransformerException( e );
1698                    }
1699                    catch ( final IOException e )
1700                    {
1701                        log( Level.SEVERE, e.getMessage(), e );
1702                        throw new TransformerException( e );
1703                    }
1704                }
1705    
1706            };
1707    
1708            transformerFactory.setErrorListener( errorListener );
1709            transformerFactory.setURIResolver( uriResolver );
1710    
1711            Integer count = 0;
1712            while ( resources.hasMoreElements() )
1713            {
1714                count++;
1715                final URL url = resources.nextElement();
1716    
1717                this.log( Level.FINE, this.getMessage( "processing", new Object[]
1718                    {
1719                        url.toExternalForm()
1720                    } ), null );
1721    
1722                final InputStream in = url.openStream();
1723                final Transformer transformer = transformerFactory.newTransformer( new StreamSource( in ) );
1724                in.close();
1725    
1726                transformer.setErrorListener( errorListener );
1727                transformer.setURIResolver( uriResolver );
1728                transformers.add( transformer );
1729            }
1730    
1731            this.log( Level.FINE, this.getMessage( "classpathReport", new Object[]
1732                {
1733                    count, Long.valueOf( System.currentTimeMillis() - t0 )
1734                } ), null );
1735    
1736            return transformers;
1737        }
1738    
1739        /**
1740         * Gets the class loader of the instance.
1741         *
1742         * @return The class loader of the instance.
1743         *
1744         * @see #setClassLoader(java.lang.ClassLoader)
1745         */
1746        public ClassLoader getClassLoader()
1747        {
1748            if ( this.classLoader == null )
1749            {
1750                this.classLoader = this.getClass().getClassLoader();
1751                if ( this.classLoader == null )
1752                {
1753                    this.classLoader = ClassLoader.getSystemClassLoader();
1754                }
1755    
1756            }
1757    
1758            return this.classLoader;
1759        }
1760    
1761        /**
1762         * Sets the class loader of the instance.
1763         *
1764         * @param value The new class loader of the instance.
1765         *
1766         * @see #getClassLoader()
1767         */
1768        public void setClassLoader( final ClassLoader value )
1769        {
1770            this.classLoader = value;
1771            this.bootstrapSchema = null;
1772            this.schema = null;
1773            this.schemas = null;
1774            this.schemaResources = null;
1775            this.entityResolver = null;
1776            this.resourceResolver = null;
1777            this.context = null;
1778        }
1779    
1780        /**
1781         * Notifies registered listeners.
1782         *
1783         * @param level The level of the event.
1784         * @param message The message of the event or {@code null}.
1785         * @param throwable The throwable of the event {@code null}.
1786         *
1787         * @see #getListeners()
1788         */
1789        protected void log( final Level level, final String message, final Throwable throwable )
1790        {
1791            for ( Listener l : this.getListeners() )
1792            {
1793                l.onLog( level, message, throwable );
1794            }
1795        }
1796    
1797        /**
1798         * Resolves references by inspecting the class loader of the instance.
1799         *
1800         * @param modules The modules to resolve.
1801         * @param cpModule The module for resolved references.
1802         *
1803         * @throws NullPointerException if {@code cpModule} is {@code null}.
1804         */
1805        private void resolveClasspath( final Modules modules, final Module cpModule )
1806        {
1807            for ( Module m : modules.getModule() )
1808            {
1809                if ( m.getSpecifications() != null )
1810                {
1811                    this.resolveClasspath( modules, m.getSpecifications(), cpModule );
1812                }
1813    
1814                if ( m.getImplementations() != null )
1815                {
1816                    this.resolveClasspath( modules, m.getImplementations(), cpModule );
1817                }
1818    
1819            }
1820        }
1821    
1822        private void resolveClasspath( final Modules modules, final SpecificationReference ref, final Module cpModule )
1823        {
1824            if ( modules.getSpecification( ref.getIdentifier() ) == null )
1825            {
1826                this.resolveClasspath( ref.getIdentifier(), cpModule );
1827            }
1828        }
1829    
1830        private void resolveClasspath( final Modules modules, final Specifications references, final Module cpModule )
1831        {
1832            for ( SpecificationReference ref : references.getReference() )
1833            {
1834                this.resolveClasspath( modules, ref, cpModule );
1835            }
1836    
1837        }
1838    
1839        private void resolveClasspath( final Modules modules, final Implementations implementations, final Module cpModule )
1840        {
1841            for ( Implementation implementation : implementations.getImplementation() )
1842            {
1843                if ( implementation.getSpecifications() != null )
1844                {
1845                    this.resolveClasspath( modules, implementation.getSpecifications(), cpModule );
1846                }
1847    
1848                if ( implementation.getDependencies() != null )
1849                {
1850                    this.resolveClasspath( modules, implementation.getDependencies(), cpModule );
1851                }
1852            }
1853        }
1854    
1855        private void resolveClasspath( final Modules modules, final Dependencies dependencies, final Module cpModule )
1856        {
1857            for ( Dependency dependency : dependencies.getDependency() )
1858            {
1859                this.resolveClasspath( modules, dependency, cpModule );
1860            }
1861        }
1862    
1863        private void resolveClasspath( final String identifier, final Module cpModule )
1864        {
1865            Specification specification =
1866                cpModule.getSpecifications() == null ? null
1867                : cpModule.getSpecifications().getSpecification( identifier );
1868    
1869            if ( specification == null )
1870            {
1871                try
1872                {
1873                    final Class classpathSpec = Class.forName( identifier, true, this.getClassLoader() );
1874                    if ( Modifier.isPublic( classpathSpec.getModifiers() ) )
1875                    {
1876                        String vendor = null;
1877                        String version = null;
1878    
1879                        if ( classpathSpec.getPackage() != null )
1880                        {
1881                            vendor = classpathSpec.getPackage().getSpecificationVendor();
1882                            version = classpathSpec.getPackage().getSpecificationVersion();
1883                        }
1884    
1885                        specification = new Specification();
1886                        specification.setIdentifier( identifier );
1887                        specification.setClazz( classpathSpec.getName() );
1888                        specification.setMultiplicity( Multiplicity.MANY );
1889                        specification.setVendor( vendor );
1890                        specification.setVersion( version );
1891    
1892                        this.log( Level.FINE, this.getMessage( "classpathSpecification", new Object[]
1893                            {
1894                                specification.getIdentifier(),
1895                                specification.getMultiplicity().value()
1896                            } ), null );
1897    
1898    
1899                        if ( cpModule.getSpecifications() == null )
1900                        {
1901                            cpModule.setSpecifications( new Specifications() );
1902                        }
1903    
1904                        cpModule.getSpecifications().getSpecification().add( specification );
1905    
1906                        this.resolveClasspath( specification, cpModule );
1907                    }
1908    
1909                }
1910                catch ( final ClassNotFoundException e )
1911                {
1912                    this.log( Level.FINE, this.getMessage( "noSuchClass", new Object[]
1913                        {
1914                            e.getMessage()
1915                        } ), null );
1916    
1917                }
1918            }
1919        }
1920    
1921        private void resolveClasspath( final Specification specification, final Module cpModule )
1922        {
1923            if ( specification == null )
1924            {
1925                throw new NullPointerException( "specification" );
1926            }
1927    
1928            Implementation implementation =
1929                cpModule.getImplementations() == null ? null
1930                : cpModule.getImplementations().getImplementation( specification.getIdentifier() );
1931    
1932            if ( implementation == null )
1933            {
1934                String name = null;
1935    
1936                try
1937                {
1938                    final Class classpathImpl = Class.forName( specification.getClazz(), true, this.getClassLoader() );
1939                    boolean classpathImplementation = false;
1940    
1941                    if ( Modifier.isPublic( classpathImpl.getModifiers() ) )
1942                    {
1943                        if ( !Modifier.isAbstract( classpathImpl.getModifiers() ) )
1944                        {
1945                            try
1946                            {
1947                                classpathImpl.getConstructor( NO_CLASSES );
1948                                name = "init";
1949                                classpathImplementation = true;
1950                            }
1951                            catch ( final NoSuchMethodException e )
1952                            {
1953                                this.log( Level.FINE, this.getMessage( "noSuchMethod", new Object[]
1954                                    {
1955                                        e.getMessage()
1956                                    } ), null );
1957    
1958                            }
1959                        }
1960    
1961                        if ( !classpathImplementation )
1962                        {
1963                            final char[] c = classpathImpl.getName().substring(
1964                                classpathImpl.getPackage().getName().length() + 1 ).toCharArray();
1965    
1966                            name = String.valueOf( c );
1967                            c[0] = Character.toUpperCase( c[0] );
1968    
1969                            if ( this.checkFactoryMethod( classpathImpl, classpathImpl, "getDefault" ) )
1970                            {
1971                                name = "default";
1972                                classpathImplementation = true;
1973                            }
1974                            else if ( this.checkFactoryMethod( classpathImpl, classpathImpl, "getInstance" ) )
1975                            {
1976                                name = "instance";
1977                                classpathImplementation = true;
1978                            }
1979                            else if ( this.checkFactoryMethod( classpathImpl, classpathImpl, "get" + String.valueOf( c ) ) )
1980                            {
1981                                classpathImplementation = true;
1982                            }
1983    
1984                        }
1985    
1986                        if ( classpathImplementation )
1987                        {
1988                            String vendor = null;
1989                            String version = null;
1990                            if ( classpathImpl.getPackage() != null )
1991                            {
1992                                vendor = classpathImpl.getPackage().getImplementationVendor();
1993                                version = classpathImpl.getPackage().getImplementationVersion();
1994                            }
1995    
1996                            implementation = new Implementation();
1997                            implementation.setVendor( vendor );
1998                            implementation.setFinal( true );
1999                            implementation.setName( name );
2000                            implementation.setIdentifier( specification.getIdentifier() );
2001                            implementation.setClazz( classpathImpl.getName() );
2002                            implementation.setVersion( version );
2003    
2004                            final Specifications implemented = new Specifications();
2005                            final SpecificationReference ref = new SpecificationReference();
2006                            ref.setIdentifier( specification.getIdentifier() );
2007                            ref.setVersion( specification.getVersion() );
2008                            implemented.getReference().add( ref );
2009                            implementation.setSpecifications( implemented );
2010    
2011                            this.log( Level.FINE, this.getMessage( "classpathImplementation", new Object[]
2012                                {
2013                                    implementation.getIdentifier(),
2014                                    specification.getIdentifier(),
2015                                    implementation.getName()
2016                                } ), null );
2017    
2018                            if ( cpModule.getImplementations() == null )
2019                            {
2020                                cpModule.setImplementations( new Implementations() );
2021                            }
2022    
2023                            cpModule.getImplementations().getImplementation().add( implementation );
2024                        }
2025                        else
2026                        {
2027                            this.log( Level.FINE, this.getMessage( "noClasspathImplementation", new Object[]
2028                                {
2029                                    specification.getIdentifier()
2030                                } ), null );
2031    
2032                        }
2033                    }
2034                }
2035                catch ( final ClassNotFoundException e )
2036                {
2037                    this.log( Level.FINE, this.getMessage( "noSuchClass", new Object[]
2038                        {
2039                            e.getMessage()
2040                        } ), null );
2041    
2042                }
2043            }
2044        }
2045    
2046        private boolean checkFactoryMethod( final Class clazz, final Class type, final String methodName )
2047        {
2048            boolean factoryMethod = false;
2049    
2050            try
2051            {
2052                final Method m = clazz.getMethod( methodName, NO_CLASSES );
2053                factoryMethod = Modifier.isStatic( m.getModifiers() ) && type.isAssignableFrom( m.getReturnType() );
2054            }
2055            catch ( final NoSuchMethodException e )
2056            {
2057                this.log( Level.FINE, this.getMessage( "noSuchMethod", new Object[]
2058                    {
2059                        e.getMessage()
2060                    } ), null );
2061    
2062                factoryMethod = false;
2063            }
2064    
2065            return factoryMethod;
2066        }
2067    
2068        private Method getFactoryMethod( final Class clazz, final String methodName )
2069        {
2070            Method m = null;
2071    
2072            try
2073            {
2074                m = clazz.getMethod( methodName, NO_CLASSES );
2075            }
2076            catch ( final NoSuchMethodException e )
2077            {
2078                this.log( Level.FINE, this.getMessage( "noSuchMethod", new Object[]
2079                    {
2080                        e.getMessage()
2081                    } ), null );
2082    
2083                m = null;
2084            }
2085    
2086            return m;
2087        }
2088    
2089        /**
2090         * Searches all available {@code META-INF/MANIFEST.MF} resources and gets a set containing URLs of entries whose
2091         * name end with a known schema extension.
2092         *
2093         * @return URLs of any matching entries.
2094         *
2095         * @throws IOException if reading or parsing fails.
2096         */
2097        private Set<URL> getSchemaResources() throws IOException
2098        {
2099            if ( this.schemaResources == null )
2100            {
2101                this.schemaResources = new HashSet<URL>();
2102    
2103                for ( final Enumeration<URL> e = this.getClassLoader().getResources( "META-INF/MANIFEST.MF" );
2104                      e.hasMoreElements(); )
2105                {
2106                    final URL manifestUrl = e.nextElement();
2107                    final String externalForm = manifestUrl.toExternalForm();
2108                    final String baseUrl = externalForm.substring( 0, externalForm.indexOf( "META-INF" ) );
2109                    final InputStream manifestStream = manifestUrl.openStream();
2110                    final Manifest mf = new Manifest( manifestStream );
2111                    manifestStream.close();
2112    
2113                    for ( Map.Entry<String, Attributes> entry : mf.getEntries().entrySet() )
2114                    {
2115                        for ( int i = SCHEMA_EXTENSIONS.length - 1; i >= 0; i-- )
2116                        {
2117                            if ( entry.getKey().toLowerCase().endsWith( '.' + SCHEMA_EXTENSIONS[i].toLowerCase() ) )
2118                            {
2119                                final URL schemaUrl = new URL( baseUrl + entry.getKey() );
2120                                this.schemaResources.add( schemaUrl );
2121                                this.log( Level.FINE, this.getMessage( "processing", new Object[]
2122                                    {
2123                                        schemaUrl.toExternalForm()
2124                                    } ), null );
2125    
2126                            }
2127                        }
2128                    }
2129                }
2130            }
2131    
2132            return this.schemaResources;
2133        }
2134    
2135        /**
2136         * Gets the implementation of an object.
2137         *
2138         * @param modules The modules to search for the implementation of {@code object}.
2139         * @param object The object to get the implementation for.
2140         *
2141         * @return The implementation for {@code object} or {@code null}, if nothing is known about {@code object}.
2142         */
2143        private Implementation getImplementation( final Modules modules, final Object object )
2144        {
2145            return this.collectImplementation( modules, object.getClass() );
2146        }
2147    
2148        private Implementation collectImplementation( final Modules modules, final Class clazz )
2149        {
2150            Implementation i = modules.getImplementation( clazz );
2151            if ( i == null && clazz.getSuperclass() != null )
2152            {
2153                i = this.collectImplementation( modules, clazz.getSuperclass() );
2154            }
2155    
2156            return i;
2157        }
2158    
2159        private String getMessage( final String key, final Object args )
2160        {
2161            return new MessageFormat(
2162                ResourceBundle.getBundle( DefaultModelManager.class.getName().replace( '.', '/' ), Locale.getDefault() ).
2163                getString( key ) ).format( args );
2164    
2165        }
2166    
2167        private void assertMessagesUniqueness( final Messages messages, final List<ModelException.Detail> details )
2168        {
2169            for ( Message m : messages.getMessage() )
2170            {
2171                if ( messages.getReference( m.getName() ) != null )
2172                {
2173                    final ModelException.Detail detail = new ModelException.Detail( Level.SEVERE, this.getMessage(
2174                        "messagesUniquessConstraint", new Object[]
2175                        {
2176                            m.getName()
2177                        } ) );
2178    
2179                    detail.setElement( this.getObjectFactory().createMessages( messages ) );
2180                    details.add( detail );
2181                }
2182            }
2183        }
2184    
2185        private void assertPropertiesUniqueness( final Properties properties, final List<ModelException.Detail> details )
2186        {
2187            for ( Property p : properties.getProperty() )
2188            {
2189                if ( properties.getReference( p.getName() ) != null )
2190                {
2191                    final ModelException.Detail detail = new ModelException.Detail( Level.SEVERE, this.getMessage(
2192                        "propertiesUniquessConstraint", new Object[]
2193                        {
2194                            p.getName()
2195                        } ) );
2196    
2197                    detail.setElement( this.getObjectFactory().createProperties( properties ) );
2198                    details.add( detail );
2199                }
2200            }
2201        }
2202    
2203        private ModelException.Detail newIncompatibleImplementationDetail(
2204            final JAXBElement<? extends ModelObject> element, final String implementation,
2205            final String implementationModule, final String specification, final String specificationModule,
2206            final String implementedVersion, final String specifiedVersion )
2207        {
2208            final ModelException.Detail detail =
2209                new ModelException.Detail( Level.SEVERE, this.getMessage( "incompatibleImplementation", new Object[]
2210                {
2211                    implementation, implementationModule, specification, specificationModule,
2212                    implementedVersion, specifiedVersion
2213                } ) );
2214    
2215            detail.setElement( element );
2216            return detail;
2217        }
2218    
2219        private ModelException.Detail newIncompatibleDependencyDetail(
2220            final JAXBElement<? extends ModelObject> element, final String implementation,
2221            final String implementationModule, final String specification, final String specificationModule,
2222            final String requiredVersion, final String availableVersion )
2223        {
2224            final ModelException.Detail detail =
2225                new ModelException.Detail( Level.SEVERE, this.getMessage( "incompatibleDependency", new Object[]
2226                {
2227                    implementation, implementationModule, specification, specificationModule,
2228                    requiredVersion, availableVersion
2229                } ) );
2230    
2231            detail.setElement( element );
2232            return detail;
2233        }
2234    
2235        private ModelException.Detail newImplementationNameConstraintDetail(
2236            final JAXBElement<? extends ModelObject> element, final String specification, final String implementations,
2237            final String nonUniqueName )
2238        {
2239            final ModelException.Detail detail =
2240                new ModelException.Detail( Level.SEVERE, this.getMessage( "implementationNameConstraint", new Object[]
2241                {
2242                    specification, implementations, nonUniqueName
2243                } ) );
2244    
2245            detail.setElement( element );
2246            return detail;
2247        }
2248    
2249        private ModelException.Detail newMandatoryDependencyConstraintDetail(
2250            final JAXBElement<? extends ModelObject> element, final String implementation, final String dependencyName )
2251        {
2252            final ModelException.Detail detail =
2253                new ModelException.Detail( Level.SEVERE, this.getMessage( "mandatoryDependencyConstraint", new Object[]
2254                {
2255                    implementation, dependencyName
2256                } ) );
2257    
2258            detail.setElement( element );
2259            return detail;
2260        }
2261    
2262        private ModelException.Detail newMultiplicityConstraintDetail(
2263            final JAXBElement<? extends ModelObject> element, final Number implementations, final String specification,
2264            final Number expected, final Multiplicity multiplicity )
2265        {
2266            final ModelException.Detail detail =
2267                new ModelException.Detail( Level.SEVERE, this.getMessage( "multiplicityConstraint", new Object[]
2268                {
2269                    implementations, specification, expected, multiplicity.value()
2270                } ) );
2271    
2272            detail.setElement( element );
2273            return detail;
2274        }
2275    
2276        private ModelException.Detail newInheritanceConstraintDetail(
2277            final JAXBElement<? extends ModelObject> element, final Implementation child, final Implementation parent )
2278        {
2279            final ModelException.Detail detail =
2280                new ModelException.Detail( Level.SEVERE, this.getMessage( "inheritanceConstraint", new Object[]
2281                {
2282                    child.getIdentifier(), parent.getIdentifier()
2283                } ) );
2284    
2285            detail.setElement( element );
2286            return detail;
2287        }
2288    
2289        private ModelException.Detail newDependencyPropertyReferenceConstraintDetail(
2290            final JAXBElement<? extends ModelObject> element, final Implementation implementation,
2291            final Dependency dependency )
2292        {
2293            final ModelException.Detail detail = new ModelException.Detail(
2294                Level.SEVERE, this.getMessage( "dependencyPropertyReferenceConstraint", new Object[]
2295                {
2296                    implementation.getIdentifier(), dependency.getName()
2297                } ) );
2298    
2299            detail.setElement( element );
2300            return detail;
2301        }
2302    
2303        private ModelException.Detail newPropertyOverwriteConstraintDetail(
2304            final JAXBElement<? extends ModelObject> element, final Implementation implementation,
2305            final Dependency dependency, final Specification specification, final String scope )
2306        {
2307            final ModelException.Detail detail = new ModelException.Detail(
2308                Level.SEVERE, this.getMessage( "propertyOverwriteConstraint", new Object[]
2309                {
2310                    implementation.getIdentifier(), dependency.getName(), specification.getIdentifier(), scope
2311                } ) );
2312    
2313            detail.setElement( element );
2314            return detail;
2315        }
2316    
2317        private ModelException.Detail newImplementationSpecificationDeclarationConstraintDetail(
2318            final JAXBElement<? extends ModelObject> element, final Implementation implementation,
2319            final Specification specification )
2320        {
2321            final ModelException.Detail detail = new ModelException.Detail(
2322                Level.SEVERE, this.getMessage( "implementationSpecificationDeclarationConstraint", new Object[]
2323                {
2324                    implementation.getIdentifier(), specification.getIdentifier()
2325                } ) );
2326    
2327            detail.setElement( element );
2328            return detail;
2329        }
2330    
2331        private ModelException.Detail newModuleSpecificationReferenceConstraintDetail(
2332            final JAXBElement<? extends ModelObject> element, final Module module, final SpecificationReference reference )
2333        {
2334            final ModelException.Detail detail = new ModelException.Detail(
2335                Level.SEVERE, this.getMessage( "moduleSpecificationReferenceConstraint", new Object[]
2336                {
2337                    module.getName(), reference.getIdentifier()
2338                } ) );
2339    
2340            detail.setElement( element );
2341            return detail;
2342        }
2343    
2344        // SECTION-END
2345    }