001/*
002 *  jDTAUS Core RI Client Container
003 *  Copyright (C) 2005 Christian Schulte
004 *  <cs@schulte.it>
005 *
006 *  This library is free software; you can redistribute it and/or
007 *  modify it under the terms of the GNU Lesser General Public
008 *  License as published by the Free Software Foundation; either
009 *  version 2.1 of the License, or any later version.
010 *
011 *  This library is distributed in the hope that it will be useful,
012 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 *  Lesser General Public License for more details.
015 *
016 *  You should have received a copy of the GNU Lesser General Public
017 *  License along with this library; if not, write to the Free Software
018 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
019 *
020 */
021package org.jdtaus.core.container.ri.client;
022
023import java.beans.Beans;
024import java.io.IOException;
025import java.io.InputStream;
026import java.lang.reflect.Constructor;
027import java.lang.reflect.InvocationTargetException;
028import java.lang.reflect.Method;
029import java.lang.reflect.Modifier;
030import java.net.URI;
031import java.net.URISyntaxException;
032import java.net.URL;
033import java.text.MessageFormat;
034import java.text.ParseException;
035import java.util.Arrays;
036import java.util.Collection;
037import java.util.Collections;
038import java.util.Comparator;
039import java.util.Enumeration;
040import java.util.HashMap;
041import java.util.HashSet;
042import java.util.Iterator;
043import java.util.LinkedList;
044import java.util.List;
045import java.util.Locale;
046import java.util.Map;
047import java.util.Set;
048import java.util.TreeMap;
049import java.util.logging.Level;
050import java.util.logging.Logger;
051import javax.xml.parsers.DocumentBuilder;
052import javax.xml.parsers.DocumentBuilderFactory;
053import javax.xml.parsers.ParserConfigurationException;
054import javax.xml.transform.ErrorListener;
055import javax.xml.transform.Transformer;
056import javax.xml.transform.TransformerConfigurationException;
057import javax.xml.transform.TransformerException;
058import javax.xml.transform.TransformerFactory;
059import javax.xml.transform.dom.DOMResult;
060import javax.xml.transform.dom.DOMSource;
061import javax.xml.transform.stream.StreamSource;
062import org.jdtaus.core.container.Argument;
063import org.jdtaus.core.container.Arguments;
064import org.jdtaus.core.container.Dependencies;
065import org.jdtaus.core.container.Dependency;
066import org.jdtaus.core.container.DuplicateSpecificationException;
067import org.jdtaus.core.container.IllegalPropertyTypeException;
068import org.jdtaus.core.container.Implementation;
069import org.jdtaus.core.container.Implementations;
070import org.jdtaus.core.container.IncompatibleImplementationException;
071import org.jdtaus.core.container.Message;
072import org.jdtaus.core.container.Messages;
073import org.jdtaus.core.container.MissingImplementationException;
074import org.jdtaus.core.container.MissingPropertyException;
075import org.jdtaus.core.container.MissingSpecificationException;
076import org.jdtaus.core.container.Model;
077import org.jdtaus.core.container.ModelError;
078import org.jdtaus.core.container.ModelObject;
079import org.jdtaus.core.container.Module;
080import org.jdtaus.core.container.Modules;
081import org.jdtaus.core.container.Properties;
082import org.jdtaus.core.container.Property;
083import org.jdtaus.core.container.PropertyOverwriteConstraintException;
084import org.jdtaus.core.container.Specification;
085import org.jdtaus.core.container.Specifications;
086import org.jdtaus.core.container.Text;
087import org.jdtaus.core.container.ri.client.versioning.VersionParser;
088import org.w3c.dom.Document;
089import org.w3c.dom.Element;
090import org.w3c.dom.NodeList;
091import org.xml.sax.ErrorHandler;
092import org.xml.sax.SAXException;
093import org.xml.sax.SAXParseException;
094
095/**
096 * {@code Model} reference implementation.
097 *
098 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
099 * @version $JDTAUS: DefaultModel.java 8743 2012-10-07 03:06:20Z schulte $
100 *
101 * @see <a href="http://xml.jdtaus.org/1.0.x/jdtaus-core/jdtaus-core-schemas/jdtaus-container-1.1.xsd">jdtaus-container-1.1.xsd</a>
102 */
103public class DefaultModel implements Model
104{
105    //--Constants---------------------------------------------------------------
106
107    /** JAXP configuration key to the Schema implementation attribute. */
108    private static final String SCHEMA_LANGUAGE_KEY =
109        "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
110
111    /** JAXP Schema implementation to use. */
112    private static final String SCHEMA_LANGUAGE =
113        "http://www.w3.org/2001/XMLSchema";
114
115    /**
116     * jDTAUS {@code Model} namespace URI for the deprecated 1.0 and 1.1 model.
117     */
118    private static final String MODEL_NS =
119        "http://jdtaus.org/runtime/container/xml";
120
121    /** {@code jDTAUS Container Model} namespace URI. */
122    private static final String CONTAINER_NS =
123        "http://jdtaus.org/core/model/container";
124
125    /** {@code jDTAUS Container Model RI} namespace URI. */
126    private static final String MODULE_NS =
127        "http://jdtaus.org/core/model/container/module";
128
129    /** Location of the document resources searched for by default. */
130    private static final String MODEL_LOCATION =
131        "META-INF/jdtaus/module.xml";
132
133    /** Location of the transformation resources searched for by default. */
134    private static final String TRANSFORMATION_LOCATION =
135        "META-INF/jdtaus/container.xslt";
136
137    /** Model versions supported by this implementation. */
138    private static final String[] SUPPORTED_MODEL_VERSIONS =
139    {
140        "1.0", "1.1", "1.2", "1.3", "1.4"
141    };
142
143    /** String constructor arguments. */
144    private static final Class[] CTOR_ARGS_STRING =
145    {
146        String.class
147    };
148
149    /** String constructor arguments. */
150    private static final Class[] CTOR_ARGS_CHAR =
151    {
152        char.class
153    };
154
155    /** Constant for the name of the platform module. */
156    private static final String PLATFORM_MODULE_NAME = Model.class.getName();
157
158    /** Constant for the version of the platform module. */
159    private static final String PLATFORM_MODULE_VERSION = "1.0";
160
161    /** Constant for model version 1.1. */
162    private static final String V_1_1 = "1.1";
163
164    /** Constant for model version 1.3. */
165    private static final String V_1_3 = "1.3";
166
167    /** Constant for model version 1.4. */
168    private static final String V_1_4 = "1.4";
169
170    /** Constant for the maximum supported model version. */
171    private static final String MODEL_VERSION = V_1_4;
172
173    //---------------------------------------------------------------Constants--
174    //--Constructors------------------------------------------------------------
175
176    /**
177     * Creates a new {@code DefaultModel} instance.
178     *
179     * @throws ModelError if no model can be read.
180     *
181     * @see #readModules()
182     */
183    public DefaultModel()
184    {
185        try
186        {
187            this.modules = this.readModules();
188            // Add the platform module.
189            final Module[] modulesWithPlatform =
190                new Module[ this.modules.size() + 1 ];
191
192            System.arraycopy( this.modules.getModules(), 0, modulesWithPlatform,
193                              0, this.modules.size() );
194
195            modulesWithPlatform[this.modules.size()] = this.getPlatformModule();
196            this.modules.setModules( modulesWithPlatform );
197
198            this.linkParentImplementations();
199            this.assertSpecificationsAvailable();
200            this.updateSpecificationReferences();
201            this.linkImplementedSpecifications();
202            this.linkDependencies();
203            this.assertCompatibility();
204            this.assertOverwrittenProperties();
205            this.assertImplementedProperties();
206        }
207        catch ( final ParserConfigurationException e )
208        {
209            throw new ModelError( e );
210        }
211        catch ( final SAXException e )
212        {
213            throw new ModelError( e );
214        }
215        catch ( final IOException e )
216        {
217            throw new ModelError( e );
218        }
219        catch ( final ParseException e )
220        {
221            throw new ModelError( e );
222        }
223        catch ( final TransformerConfigurationException e )
224        {
225            throw new ModelError( e );
226        }
227        catch ( final TransformerException e )
228        {
229            throw new ModelError( e );
230        }
231        catch ( final URISyntaxException e )
232        {
233            throw new ModelError( e );
234        }
235        finally
236        {
237            this.specifications = null;
238            this.implementations = null;
239            this.dependencies = null;
240            this.impl2parent = null;
241            this.moduleMap = null;
242        }
243    }
244
245    //------------------------------------------------------------Constructors--
246    //--Model-------------------------------------------------------------------
247
248    public Modules getModules()
249    {
250        return this.modules;
251    }
252
253    //-------------------------------------------------------------------Model--
254    //--DefaultModel------------------------------------------------------------
255
256    /** Logger of the class. */
257    private static final Logger LOGGER =
258        Logger.getLogger( DefaultModel.class.getName() );
259
260    /** Holds the instance of the platform module. */
261    private Module platformModule;
262
263    /** Holds the loaded model. */
264    private Modules modules;
265
266    /**
267     * Maps specification identifiers to a list of specification instances by
268     * version.
269     */
270    private Map/*<String,Collection>*/ specifications = new HashMap( 1000 );
271
272    /** Maps implementation identifiers to implementation instances. */
273    private Map/*<String,Implementation>*/ implementations = new HashMap( 1000 );
274
275    /**
276     * Maps identifiers of implementations to identifiers of parent
277     * implementations.
278     */
279    private Map/*<String,String>*/ impl2parent = new HashMap( 1000 );
280
281    /** Maps dependency keys to implementation names. */
282    private Map/*<String,String>*/ dependencies = new HashMap( 1000 );
283
284    /** Maps module names to resource URLs. */
285    private Map/*<String,URL>*/ moduleMap = new HashMap( 1000 );
286
287    /**
288     * Reads the module resources.
289     *
290     * @return all read modules without resolved references.
291     *
292     * @throws IOException if reading fails.
293     * @throws ParserConfigurationException if configuring the parser fails.
294     * @throws SAXException if parsing documents fails.
295     * @throws ParseException if parsing versions fails.
296     * @throws TransformerException if creating a transformer fails or if
297     * transforming documents fails.
298     * @throws URISyntaxException if creating a resource URI fails.
299     */
300    private Modules readModules()
301        throws IOException, ParserConfigurationException, SAXException,
302               ParseException, TransformerException, URISyntaxException
303    {
304        final Map documents = new HashMap();
305        final DocumentBuilderFactory xmlFactory =
306            DocumentBuilderFactory.newInstance();
307
308        final Enumeration resources = ClassLoaderFactory.loadResources(
309            this.getClass(), MODEL_LOCATION );
310
311        xmlFactory.setNamespaceAware( true );
312
313        try
314        {
315            xmlFactory.setValidating( true );
316            xmlFactory.setAttribute( SCHEMA_LANGUAGE_KEY, SCHEMA_LANGUAGE );
317        }
318        catch ( final IllegalArgumentException e )
319        {
320            LOGGER.log( Level.CONFIG, e.getMessage() );
321            LOGGER.log( Level.WARNING, DefaultModelBundle.getInstance().
322                getNoValidationWarningMessage( Locale.getDefault(),
323                                               e.getMessage() ) );
324
325            xmlFactory.setValidating( false );
326        }
327
328        final DocumentBuilder xmlBuilder = xmlFactory.newDocumentBuilder();
329        xmlBuilder.setEntityResolver( new BootstrapEntityResolver() );
330
331        // Parse and validate all files from the classpath.
332        while ( resources.hasMoreElements() )
333        {
334            final URL resource = (URL) resources.nextElement();
335
336            if ( LOGGER.isLoggable( Level.FINE ) )
337            {
338                LOGGER.log( Level.FINE, DefaultModelBundle.getInstance().
339                    getResourceInformationMessage(
340                    Locale.getDefault(), resource.toExternalForm() ) );
341
342            }
343
344            final InputStream stream = resource.openStream();
345            xmlBuilder.setErrorHandler(
346                new ErrorHandler()
347                {
348
349                    public void warning( final SAXParseException e )
350                    {
351                        LOGGER.log( Level.WARNING,
352                                    DefaultModelBundle.getInstance().
353                            getParseExceptionMessage(
354                            Locale.getDefault(), resource.toExternalForm(),
355                            e.getMessage(), new Integer( e.getLineNumber() ),
356                            new Integer( e.getColumnNumber() ) ) );
357
358                    }
359
360                    public void fatalError( final SAXParseException e )
361                        throws SAXException
362                    {
363                        throw new SAXException(
364                            DefaultModelBundle.getInstance().
365                            getParseExceptionMessage(
366                            Locale.getDefault(), resource.toExternalForm(),
367                            e.getMessage(), new Integer( e.getLineNumber() ),
368                            new Integer( e.getColumnNumber() ) ), e );
369
370                    }
371
372                    public void error( final SAXParseException e )
373                        throws SAXException
374                    {
375                        throw new SAXException(
376                            DefaultModelBundle.getInstance().
377                            getParseExceptionMessage(
378                            Locale.getDefault(), resource.toExternalForm(),
379                            e.getMessage(), new Integer( e.getLineNumber() ),
380                            new Integer( e.getColumnNumber() ) ), e );
381
382                    }
383
384                } );
385
386            final Document doc = xmlBuilder.parse( stream );
387            stream.close();
388            documents.put( new URI( resource.toString() ), doc );
389        }
390
391        // Transform the XML documents.
392        return this.transformDocuments( documents );
393    }
394
395    private void updateSpecificationReferences()
396    {
397        for ( int m = this.modules.size() - 1; m >= 0; m-- )
398        {
399            final Module module = this.modules.getModule( m );
400            for ( int s = module.getSpecifications().size() - 1; s >= 0; s-- )
401            {
402                final Specification spec = module.getSpecifications().
403                    getSpecification( s );
404
405                final Collection references =
406                    (Collection) this.specifications.get( spec.getIdentifier() );
407
408                assert references != null : "Expected specification meta-data.";
409
410                for ( final Iterator it = references.iterator(); it.hasNext(); )
411                {
412                    final Specification reference = (Specification) it.next();
413                    reference.setModuleName( module.getName() );
414
415                    if ( !reference.equals( spec ) )
416                    {
417                        reference.setDocumentation( spec.getDocumentation() );
418                        reference.setMultiplicity( spec.getMultiplicity() );
419                        reference.setProperties( spec.getProperties() );
420                        reference.setScope( spec.getScope() );
421                        reference.setStateless( spec.isStateless() );
422                        reference.setVendor( spec.getVendor() );
423                        reference.setModelVersion( spec.getModelVersion() );
424                    }
425                }
426            }
427        }
428    }
429
430    private void linkParentImplementations()
431    {
432        for ( final Iterator it = this.implementations.entrySet().iterator();
433              it.hasNext(); )
434        {
435            final Map.Entry e = (Map.Entry) it.next();
436            final String identifier = (String) e.getKey();
437            final Implementation implementation = (Implementation) e.getValue();
438            final String parentIdentifier =
439                (String) this.impl2parent.get( identifier );
440
441            if ( parentIdentifier != null )
442            {
443                final Implementation parent =
444                    (Implementation) this.implementations.get(
445                    parentIdentifier );
446
447                if ( parent == null )
448                {
449                    throw new MissingImplementationException(
450                        parentIdentifier );
451
452                }
453
454                implementation.setParent( parent );
455            }
456        }
457    }
458
459    private void linkImplementedSpecifications()
460    {
461        for ( final Iterator it = this.implementations.values().iterator();
462              it.hasNext(); )
463        {
464            final Implementation implementation = (Implementation) it.next();
465
466            for ( int i = implementation.getImplementedSpecifications().
467                size() - 1; i >= 0; i-- )
468            {
469                final Specification implemented =
470                    implementation.getImplementedSpecifications().
471                    getSpecification( i );
472
473                final Collection specs =
474                    (Collection) this.specifications.get(
475                    implemented.getIdentifier() );
476
477                assert specs != null : "Expected specification meta-data.";
478
479                for ( final Iterator s = specs.iterator(); s.hasNext(); )
480                {
481                    final Specification spec = (Specification) s.next();
482                    final Collection col = new LinkedList(
483                        Arrays.asList( spec.getImplementations().
484                        getImplementations() ) );
485
486                    col.add( implementation );
487
488                    final Implementations impls = new Implementations();
489                    impls.setImplementations(
490                        (Implementation[]) col.toArray(
491                        new Implementation[ col.size() ] ) );
492
493                    spec.setImplementations( impls );
494                }
495            }
496        }
497    }
498
499    private void linkDependencies()
500    {
501        for ( final Iterator it = this.implementations.values().iterator();
502              it.hasNext(); )
503        {
504            final Implementation implementation = (Implementation) it.next();
505            for ( int i = implementation.getDeclaredDependencies().size() - 1;
506                  i >= 0; i-- )
507            {
508                final Dependency d = implementation.getDeclaredDependencies().
509                    getDependency( i );
510
511                final String key =
512                    implementation.getIdentifier() + '/' + d.getName();
513
514                final String name = (String) this.dependencies.get( key );
515
516                if ( name != null )
517                {
518                    d.setImplementation( d.getSpecification().
519                        getImplementation( name ) );
520
521                }
522            }
523        }
524    }
525
526    private void assertSpecificationsAvailable()
527    {
528        final Implementation[] impls = this.modules.getImplementations().
529            getImplementations();
530
531        for ( int i = impls.length - 1; i >= 0; i-- )
532        {
533            final Specifications specs = impls[i].getImplementedSpecifications();
534            final Dependencies deps = impls[i].getDependencies();
535
536            for ( int j = specs.size() - 1; j >= 0; j-- )
537            {
538                try
539                {
540                    this.modules.getSpecification(
541                        specs.getSpecification( j ).getIdentifier() );
542
543                }
544                catch ( final MissingSpecificationException e )
545                {
546                    if ( !this.addPlatformSpecification(
547                        specs.getSpecification( j ).getIdentifier(), true ) )
548                    {
549                        throw e;
550                    }
551                }
552            }
553
554            for ( int j = deps.size() - 1; j >= 0; j-- )
555            {
556                try
557                {
558                    this.modules.getSpecification(
559                        deps.getDependency( j ).getSpecification().
560                        getIdentifier() );
561
562                }
563                catch ( final MissingSpecificationException e )
564                {
565                    if ( !this.addPlatformSpecification(
566                        deps.getDependency( j ).getSpecification().
567                        getIdentifier(), true ) )
568                    {
569                        throw e;
570                    }
571                }
572            }
573        }
574    }
575
576    private void assertCompatibility() throws ParseException
577    {
578        for ( int i = this.modules.getImplementations().size() - 1; i >= 0;
579              i-- )
580        {
581            final Implementation impl = this.modules.getImplementations().
582                getImplementation( i );
583
584            if ( impl.getModelVersion() == null ||
585                 VersionParser.compare( impl.getModelVersion(), V_1_3 ) < 0 )
586            {
587                continue;
588            }
589
590            final Specifications specs = impl.getImplementedSpecifications();
591            final Dependencies deps = impl.getDependencies();
592
593            for ( int s = specs.size() - 1; s >= 0; s-- )
594            {
595                final Specification implemented = specs.getSpecification( s );
596                final Specification available =
597                    this.modules.getSpecification( implemented.getIdentifier() );
598
599                if ( available.getModelVersion() == null ||
600                     VersionParser.compare( available.getModelVersion(),
601                                            V_1_3 ) < 0 )
602                {
603                    continue;
604                }
605
606                if ( implemented.getVersion() != null )
607                {
608                    try
609                    {
610                        if ( VersionParser.compare(
611                            implemented.getVersion(),
612                            available.getVersion() ) < 0 )
613                        {
614                            throw new IncompatibleImplementationException(
615                                available.getIdentifier(),
616                                available.getVersion(),
617                                impl.getIdentifier(), implemented.getVersion(),
618                                null );
619
620                        }
621                    }
622                    catch ( final ParseException e )
623                    {
624                        final IncompatibleImplementationException iie =
625                            new IncompatibleImplementationException(
626                            available.getIdentifier(), available.getVersion(),
627                            impl.getIdentifier(), implemented.getVersion(),
628                            null );
629
630                        iie.initCause( e );
631                        throw iie;
632                    }
633                }
634            }
635
636            for ( int d = deps.size() - 1; d >= 0; d-- )
637            {
638                final Specification required =
639                    deps.getDependency( d ).getSpecification();
640
641                final Specification available =
642                    this.modules.getSpecification( required.getIdentifier() );
643
644                if ( available.getModelVersion() == null ||
645                     VersionParser.compare( available.getModelVersion(),
646                                            V_1_3 ) < 0 )
647                {
648                    continue;
649                }
650
651                if ( required.getVersion() != null )
652                {
653                    try
654                    {
655                        if ( VersionParser.compare(
656                            available.getVersion(),
657                            required.getVersion() ) < 0 )
658                        {
659                            throw new IncompatibleImplementationException(
660                                available.getIdentifier(),
661                                available.getVersion(),
662                                impl.getIdentifier(), null,
663                                required.getVersion() );
664
665                        }
666                    }
667                    catch ( final ParseException e )
668                    {
669                        final IncompatibleImplementationException iie =
670                            new IncompatibleImplementationException(
671                            available.getIdentifier(), available.getVersion(),
672                            impl.getIdentifier(), null, required.getVersion() );
673
674                        iie.initCause( e );
675                        throw iie;
676                    }
677                }
678            }
679        }
680    }
681
682    private void assertOverwrittenProperties() throws ParseException
683    {
684        for ( int i = this.modules.getImplementations().size() - 1; i >= 0;
685              i-- )
686        {
687            final Implementation impl = this.modules.getImplementations().
688                getImplementation( i );
689
690            final Dependencies deps = impl.getDependencies();
691            for ( int d = deps.size() - 1; d >= 0; d-- )
692            {
693                final Dependency dep = deps.getDependency( d );
694                if ( dep.getSpecification().getScope() !=
695                     Specification.SCOPE_MULTITON &&
696                     dep.getDeclaredProperties().size() > 0 )
697                {
698                    throw new PropertyOverwriteConstraintException(
699                        impl.getIdentifier(), dep.getName() );
700
701                }
702
703                final Properties properties = dep.getDeclaredProperties();
704                for ( int p = properties.size() - 1; p >= 0; p-- )
705                {
706                    final Property dependencyProperty =
707                        properties.getProperty( p );
708
709                    try
710                    {
711                        final Property specificationProperty =
712                            dep.getSpecification().getProperties().
713                            getProperty( dependencyProperty.getName() );
714
715                        if ( !dependencyProperty.getType().
716                            equals( specificationProperty.getType() ) )
717                        {
718                            throw new IllegalPropertyTypeException(
719                                dependencyProperty.getName(),
720                                dependencyProperty.getType(),
721                                specificationProperty.getType() );
722
723                        }
724
725                        dependencyProperty.setApi( true );
726                    }
727                    catch ( final MissingPropertyException e )
728                    {
729                        if ( VersionParser.compare(
730                            dep.getSpecification().getModelVersion(),
731                            V_1_3 ) >= 0 )
732                        {
733                            final PropertyOverwriteConstraintException poce =
734                                new PropertyOverwriteConstraintException(
735                                impl.getIdentifier(), dep.getName() );
736
737                            poce.initCause( e );
738                            throw poce;
739                        }
740                    }
741                }
742            }
743        }
744    }
745
746    private void assertImplementedProperties() throws ParseException
747    {
748        for ( int i = this.modules.getImplementations().size() - 1; i >= 0;
749              i-- )
750        {
751            final Map properties = new HashMap( 100 );
752            final Implementation impl = this.modules.getImplementations().
753                getImplementation( i );
754
755            final Specifications specs = impl.getImplementedSpecifications();
756            for ( int s = specs.size() - 1; s >= 0; s-- )
757            {
758                final Specification implementedSpec =
759                    specs.getSpecification( s );
760
761                final Properties props = implementedSpec.getProperties();
762                for ( int p = props.size() - 1; p >= 0; p-- )
763                {
764                    final Property implementedProperty =
765                        props.getProperty( p );
766
767                    final Property alreadyImplemented =
768                        (Property) properties.get(
769                        implementedProperty.getName() );
770
771                    if ( alreadyImplemented != null )
772                    {
773                        if ( !implementedProperty.getType().
774                            equals( alreadyImplemented.getType() ) )
775                        {
776                            throw new IllegalPropertyTypeException(
777                                implementedProperty.getName(),
778                                implementedProperty.getType(),
779                                alreadyImplemented.getType() );
780
781                        }
782                    }
783                    else
784                    {
785                        properties.put( implementedProperty.getName(),
786                                        implementedProperty );
787
788                    }
789
790                    try
791                    {
792                        final Property property =
793                            impl.getProperties().getProperty(
794                            implementedProperty.getName() );
795
796                        if ( !property.getType().
797                            equals( implementedProperty.getType() ) )
798                        {
799                            throw new IllegalPropertyTypeException(
800                                property.getName(), property.getType(),
801                                implementedProperty.getType() );
802
803                        }
804                    }
805                    catch ( final MissingPropertyException e )
806                    {
807                        if ( VersionParser.compare( impl.getModelVersion(),
808                                                    V_1_3 ) >= 0 )
809                        {
810                            final PropertyOverwriteConstraintException poce =
811                                new PropertyOverwriteConstraintException(
812                                impl.getIdentifier(),
813                                implementedSpec.getIdentifier(),
814                                implementedProperty.getName() );
815
816                            poce.initCause( e );
817                            throw poce;
818                        }
819                    }
820                }
821            }
822
823            for ( int p = impl.getProperties().size() - 1; p >= 0; p-- )
824            {
825                final Property property = impl.getProperties().getProperty( p );
826                final Property specified =
827                    (Property) properties.get( property.getName() );
828
829                if ( specified != null &&
830                     !property.getType().equals( specified.getType() ) )
831                {
832                    throw new IllegalPropertyTypeException(
833                        property.getName(), property.getType(),
834                        specified.getType() );
835
836                }
837            }
838        }
839    }
840
841    private Specification getSpecification( final String identifier,
842                                            final String version )
843    {
844        Collection c = (Collection) this.specifications.get( identifier );
845        if ( c == null )
846        {
847            c = new LinkedList();
848            this.specifications.put( identifier, c );
849        }
850
851        Specification specification = null;
852        for ( final Iterator it = c.iterator(); it.hasNext(); )
853        {
854            final Specification s = (Specification) it.next();
855
856            if ( s.getVersion() == null
857                 ? version == null
858                 : s.getVersion().equals( version ) )
859            {
860                specification = s;
861                break;
862            }
863        }
864
865        if ( specification == null )
866        {
867            specification = new Specification();
868            specification.setIdentifier( identifier );
869            specification.setVersion( version );
870            c.add( specification );
871        }
872
873        return specification;
874    }
875
876    private Implementation getImplementation( final String identifier )
877    {
878        Implementation implementation =
879            (Implementation) this.implementations.get( identifier );
880
881        if ( implementation == null )
882        {
883            implementation = new Implementation();
884            implementation.setIdentifier( identifier );
885            this.implementations.put( identifier, implementation );
886        }
887
888        return implementation;
889    }
890
891    private Modules transformDocuments( final Map xml )
892        throws ParseException, IOException, TransformerException
893    {
894        final Modules mods = new Modules();
895        mods.setModelVersion( MODEL_VERSION );
896
897        final List list = new LinkedList();
898
899        for ( final Iterator it = xml.entrySet().iterator(); it.hasNext(); )
900        {
901            final Map.Entry entry = (Map.Entry) it.next();
902            Document doc = (Document) entry.getValue();
903
904            final TransformerFactory f = TransformerFactory.newInstance();
905            final Enumeration transformers = ClassLoaderFactory.loadResources(
906                this.getClass(), TRANSFORMATION_LOCATION );
907
908            while ( transformers.hasMoreElements() )
909            {
910                final URL rsrc = (URL) transformers.nextElement();
911
912                if ( LOGGER.isLoggable( Level.CONFIG ) )
913                {
914                    LOGGER.log( Level.CONFIG, DefaultModelBundle.getInstance().
915                        getResourceInformationMessage(
916                        Locale.getDefault(), rsrc.toExternalForm() ) );
917
918                }
919
920                f.setErrorListener( new ErrorListener()
921                {
922
923                    public void warning( final TransformerException e )
924                        throws TransformerException
925                    {
926                        LOGGER.log( Level.WARNING,
927                                    DefaultModelBundle.getInstance().
928                            getParseExceptionMessage(
929                            Locale.getDefault(), rsrc.toExternalForm(),
930                            e.getMessage(),
931                            new Integer( e.getLocator().getLineNumber() ),
932                            new Integer( e.getLocator().getColumnNumber() ) ) );
933
934                    }
935
936                    public void error( final TransformerException e )
937                        throws TransformerException
938                    {
939                        throw new TransformerException(
940                            DefaultModelBundle.getInstance().
941                            getParseExceptionMessage(
942                            Locale.getDefault(), rsrc.toExternalForm(),
943                            e.getMessage(),
944                            new Integer( e.getLocator().getLineNumber() ),
945                            new Integer( e.getLocator().getColumnNumber() ) ),
946                            e );
947
948                    }
949
950                    public void fatalError( final TransformerException e )
951                        throws TransformerException
952                    {
953                        throw new TransformerException(
954                            DefaultModelBundle.getInstance().
955                            getParseExceptionMessage(
956                            Locale.getDefault(), rsrc.toExternalForm(),
957                            e.getMessage(),
958                            new Integer( e.getLocator().getLineNumber() ),
959                            new Integer( e.getLocator().getColumnNumber() ) ),
960                            e );
961
962                    }
963
964                } );
965
966                final Transformer t =
967                    f.newTransformer( new StreamSource( rsrc.openStream() ) );
968
969                final DOMSource source = new DOMSource( doc );
970                final DOMResult result = new DOMResult();
971                t.transform( source, result );
972
973                doc = (Document) result.getNode();
974            }
975
976            final URL documentResource = ( (URI) entry.getKey() ).toURL();
977            final Element e = doc.getDocumentElement();
978            String namespace = doc.getDocumentElement().getNamespaceURI();
979
980            if ( namespace == null )
981            {
982                throw new ModelError(
983                    DefaultModelBundle.getInstance().
984                    getUnknownNamespaceMessage(
985                    Locale.getDefault(), namespace,
986                    doc.getDocumentElement().getNodeName() ) );
987
988            }
989
990            final boolean deprecatedModel;
991            final String modelVersion;
992
993            if ( MODEL_NS.equals( namespace ) )
994            {
995                deprecatedModel = true;
996
997                if ( !e.hasAttributeNS( namespace, "version" ) )
998                {
999                    throw new ModelError(
1000                        DefaultModelBundle.getInstance().
1001                        getMissingAttributeMessage(
1002                        Locale.getDefault(), "version", e.getLocalName() ) );
1003
1004                }
1005
1006                modelVersion = e.getAttributeNS( namespace, "version" );
1007            }
1008            else if ( MODULE_NS.equals( namespace ) )
1009            {
1010                deprecatedModel = true;
1011                namespace = CONTAINER_NS;
1012
1013                if ( !e.hasAttributeNS( MODULE_NS, "modelVersion" ) )
1014                {
1015                    throw new ModelError(
1016                        DefaultModelBundle.getInstance().
1017                        getMissingAttributeMessage(
1018                        Locale.getDefault(), "modelVersion",
1019                        e.getLocalName() ) );
1020
1021                }
1022
1023                modelVersion = e.getAttributeNS( MODULE_NS, "modelVersion" );
1024            }
1025            else if ( CONTAINER_NS.equals( namespace ) )
1026            {
1027                deprecatedModel = false;
1028
1029                if ( !e.hasAttributeNS( CONTAINER_NS, "modelVersion" ) )
1030                {
1031                    throw new ModelError(
1032                        DefaultModelBundle.getInstance().
1033                        getMissingAttributeMessage(
1034                        Locale.getDefault(), "modelVersion",
1035                        e.getLocalName() ) );
1036
1037                }
1038
1039                modelVersion = e.getAttributeNS( CONTAINER_NS, "modelVersion" );
1040            }
1041            else
1042            {
1043                throw new ModelError(
1044                    DefaultModelBundle.getInstance().
1045                    getUnknownNamespaceMessage(
1046                    Locale.getDefault(), namespace,
1047                    doc.getDocumentElement().getLocalName() ) );
1048
1049            }
1050
1051            boolean versionSupported = false;
1052            if ( modelVersion != null )
1053            {
1054                for ( int i = SUPPORTED_MODEL_VERSIONS.length - 1; i >= 0; i-- )
1055                {
1056                    if ( SUPPORTED_MODEL_VERSIONS[i].equals( modelVersion ) )
1057                    {
1058                        versionSupported = true;
1059                        break;
1060                    }
1061                }
1062            }
1063
1064            if ( !versionSupported )
1065            {
1066                throw new ModelError(
1067                    DefaultModelBundle.getInstance().
1068                    getUnsupportedModelversionMessage(
1069                    Locale.getDefault(), modelVersion ) );
1070
1071            }
1072
1073            if ( VersionParser.compare( modelVersion, V_1_3 ) >= 0 )
1074            {
1075                if ( e.getLocalName().equals( "module" ) )
1076                {
1077                    final Module m = this.transformModule(
1078                        modelVersion, namespace, e, documentResource );
1079
1080                    if ( m != null )
1081                    {
1082                        list.add( m );
1083                    }
1084                }
1085                else if ( e.getLocalName().equals( "modules" ) )
1086                {
1087                    final NodeList l = e.getElementsByTagNameNS( namespace,
1088                                                                 "module" );
1089
1090                    for ( int i = l.getLength() - 1; i >= 0; i-- )
1091                    {
1092                        if ( l.item( i ).getParentNode().equals( e ) )
1093                        {
1094                            final Module m = this.transformModule(
1095                                modelVersion, namespace, (Element) l.item( i ),
1096                                documentResource );
1097
1098                            if ( m != null )
1099                            {
1100                                list.add( m );
1101                            }
1102                        }
1103                    }
1104                }
1105                else
1106                {
1107                    throw new ModelError(
1108                        DefaultModelBundle.getInstance().
1109                        getUnsupportedElementMessage( Locale.getDefault(),
1110                                                      e.getLocalName() ) );
1111
1112                }
1113            }
1114            else
1115            {
1116                final Module m = this.transformModule( modelVersion, namespace,
1117                                                       e, documentResource );
1118
1119                if ( m != null )
1120                {
1121                    list.add( m );
1122                }
1123            }
1124
1125            if ( deprecatedModel )
1126            {
1127                if ( LOGGER.isLoggable( Level.CONFIG ) )
1128                {
1129                    LOGGER.log( Level.CONFIG, DefaultModelBundle.getInstance().
1130                        getDeprecatedModelMessage( Locale.getDefault(),
1131                                                   namespace,
1132                                                   modelVersion ) );
1133
1134                }
1135            }
1136        }
1137
1138        // Merge any provided platform modules.
1139        Module providedPlatformModule = null;
1140        for ( final Iterator it = list.iterator(); it.hasNext(); )
1141        {
1142            final Module module = (Module) it.next();
1143            if ( PLATFORM_MODULE_NAME.equals( module.getName() ) )
1144            {
1145                providedPlatformModule = module;
1146                it.remove();
1147                break;
1148            }
1149        }
1150
1151        mods.setModules(
1152            (Module[]) list.toArray( new Module[ list.size() ] ) );
1153
1154        this.modules = mods;
1155
1156        if ( providedPlatformModule != null &&
1157             providedPlatformModule.getSpecifications() != null )
1158        {
1159            for ( int i = providedPlatformModule.getSpecifications().size() - 1;
1160                  i >= 0; i-- )
1161            {
1162                this.addPlatformSpecification(
1163                    providedPlatformModule.getSpecifications().
1164                    getSpecification( i ).getIdentifier(), true );
1165
1166            }
1167        }
1168
1169        return mods;
1170    }
1171
1172    private Specifications transformSpecifications(
1173        final String modelVersion, final String namespace,
1174        final Element specificationsElement ) throws ParseException
1175    {
1176        final List list = new LinkedList();
1177        NodeList l =
1178            specificationsElement.getElementsByTagNameNS( namespace,
1179                                                          "specification" );
1180
1181        if ( l != null && l.getLength() > 0 )
1182        {
1183            for ( int i = l.getLength() - 1; i >= 0; i-- )
1184            {
1185                if ( l.item( i ).getParentNode().
1186                    equals( specificationsElement ) )
1187                {
1188                    list.add( this.transformSpecification(
1189                        modelVersion, namespace, (Element) l.item( i ) ) );
1190
1191                }
1192            }
1193        }
1194
1195        if ( VersionParser.compare( modelVersion, V_1_3 ) >= 0 )
1196        {
1197            l = specificationsElement.getElementsByTagNameNS( namespace,
1198                                                              "reference" );
1199
1200            if ( l != null && l.getLength() > 0 )
1201            {
1202                for ( int i = l.getLength() - 1; i >= 0; i-- )
1203                {
1204                    if ( l.item( i ).getParentNode().
1205                        equals( specificationsElement ) )
1206                    {
1207                        list.add(
1208                            this.transformSpecificationReference(
1209                            namespace, (Element) l.item( i ) ) );
1210
1211                    }
1212                }
1213            }
1214        }
1215
1216        final Specifications specs = new Specifications();
1217        specs.setModelVersion( modelVersion );
1218        specs.setSpecifications( (Specification[]) list.toArray(
1219            new Specification[ list.size() ] ) );
1220
1221        if ( VersionParser.compare( modelVersion, V_1_3 ) >= 0 )
1222        {
1223            this.transformModelObject( namespace, specificationsElement, specs );
1224        }
1225
1226        return specs;
1227    }
1228
1229    private Implementations transformImplementations(
1230        final String modelVersion, final String namespace,
1231        final Module module, final Element implementationsElement )
1232        throws ParseException
1233    {
1234        final List list = new LinkedList();
1235        final NodeList l =
1236            implementationsElement.getElementsByTagNameNS( namespace,
1237                                                           "implementation" );
1238
1239        if ( l != null && l.getLength() > 0 )
1240        {
1241            for ( int i = l.getLength() - 1; i >= 0; i-- )
1242            {
1243                if ( l.item( i ).getParentNode().
1244                    equals( implementationsElement ) )
1245                {
1246                    list.add( this.transformImplementation(
1247                        modelVersion, namespace, module,
1248                        (Element) l.item( i ) ) );
1249
1250                }
1251            }
1252        }
1253
1254        final Implementations impls = new Implementations();
1255        impls.setModelVersion( modelVersion );
1256        impls.setImplementations( (Implementation[]) list.toArray(
1257            new Implementation[ list.size() ] ) );
1258
1259        if ( VersionParser.compare( modelVersion, V_1_3 ) >= 0 )
1260        {
1261            this.transformModelObject( namespace, implementationsElement,
1262                                       impls );
1263
1264        }
1265
1266        return impls;
1267    }
1268
1269    private Properties transformProperties(
1270        final String modelVersion, final String namespace,
1271        final Element propertiesElement )
1272        throws ParseException
1273    {
1274        final Map transformed = new TreeMap();
1275        final NodeList l =
1276            propertiesElement.getElementsByTagNameNS( namespace, "property" );
1277
1278        if ( l != null && l.getLength() > 0 )
1279        {
1280            for ( int i = l.getLength() - 1; i >= 0; i-- )
1281            {
1282                if ( l.item( i ).getParentNode().equals( propertiesElement ) )
1283                {
1284                    final Property p = this.transformProperty(
1285                        modelVersion, namespace, (Element) l.item( i ) );
1286
1287                    if ( propertiesElement.getParentNode().getLocalName().
1288                        equals( "specification" ) )
1289                    {
1290                        p.setValue( null );
1291                        p.setApi( true );
1292                    }
1293
1294                    transformed.put( p.getName(), p );
1295                }
1296            }
1297        }
1298
1299        final Properties props = new Properties();
1300        props.setModelVersion( modelVersion );
1301        props.setProperties( (Property[]) transformed.values().toArray(
1302            new Property[ transformed.size() ] ) );
1303
1304        if ( VersionParser.compare( modelVersion, V_1_3 ) >= 0 )
1305        {
1306            this.transformModelObject( namespace, propertiesElement, props );
1307        }
1308
1309        return props;
1310    }
1311
1312    private Dependencies transformDependencies(
1313        final String modelVersion, final String namespace,
1314        final String implementationIdentifier,
1315        final Element dependenciesElement ) throws ParseException
1316    {
1317        final List list = new LinkedList();
1318        final NodeList l =
1319            dependenciesElement.getElementsByTagNameNS( namespace,
1320                                                        "dependency" );
1321
1322        if ( l != null && l.getLength() > 0 )
1323        {
1324            for ( int i = l.getLength() - 1; i >= 0; i-- )
1325            {
1326                if ( l.item( i ).getParentNode().equals( dependenciesElement ) )
1327                {
1328                    list.add(
1329                        this.transformDependency( modelVersion, namespace,
1330                                                  implementationIdentifier,
1331                                                  (Element) l.item( i ) ) );
1332
1333                }
1334            }
1335        }
1336
1337        final Dependencies deps = new Dependencies();
1338        deps.setModelVersion( modelVersion );
1339        deps.setDependencies( (Dependency[]) list.toArray(
1340            new Dependency[ list.size() ] ) );
1341
1342        if ( VersionParser.compare( modelVersion, V_1_3 ) >= 0 )
1343        {
1344            this.transformModelObject( namespace, dependenciesElement, deps );
1345        }
1346
1347        return deps;
1348    }
1349
1350    private Messages transformMessages(
1351        final String modelVersion, final String namespace, final Module module,
1352        final Element messagesElement )
1353    {
1354        final List messages = new LinkedList();
1355        NodeList l = messagesElement.getElementsByTagNameNS( namespace,
1356                                                             "message" );
1357
1358        if ( l != null && l.getLength() > 0 )
1359        {
1360            for ( int i = l.getLength() - 1; i >= 0; i-- )
1361            {
1362                if ( l.item( i ).getParentNode().equals( messagesElement ) )
1363                {
1364                    messages.add(
1365                        this.transformMessage( modelVersion, module.getName(),
1366                                               namespace,
1367                                               (Element) l.item( i ) ) );
1368
1369                }
1370            }
1371        }
1372
1373        l = messagesElement.getElementsByTagNameNS( namespace,
1374                                                    "reference" );
1375
1376        if ( l != null && l.getLength() > 0 )
1377        {
1378            for ( int i = l.getLength() - 1; i >= 0; i-- )
1379            {
1380                if ( l.item( i ).getParentNode().equals( messagesElement ) )
1381                {
1382                    final String name =
1383                        ( (Element) l.item( i ) ).getAttributeNS(
1384                        namespace, "name" );
1385
1386                    messages.add( module.getMessages().getMessage( name ) );
1387                }
1388            }
1389        }
1390
1391        final Messages msgs = new Messages();
1392        msgs.setModelVersion( modelVersion );
1393        msgs.setMessages( (Message[]) messages.toArray(
1394            new Message[ messages.size() ] ) );
1395
1396        this.transformModelObject( namespace, messagesElement, msgs );
1397
1398        return msgs;
1399    }
1400
1401    private Arguments transformArguments(
1402        final String modelVersion, final String namespace,
1403        final Element argumentsElement )
1404    {
1405        final List arguments = new LinkedList();
1406        final NodeList l =
1407            argumentsElement.getElementsByTagNameNS( namespace,
1408                                                     "argument" );
1409
1410        if ( l != null && l.getLength() > 0 )
1411        {
1412            for ( int i = l.getLength() - 1; i >= 0; i-- )
1413            {
1414                if ( l.item( i ).getParentNode().equals( argumentsElement ) )
1415                {
1416                    arguments.add(
1417                        this.transformArgument( modelVersion, namespace,
1418                                                (Element) l.item( i ) ) );
1419
1420                }
1421            }
1422        }
1423
1424        Collections.sort( arguments, new Comparator()
1425        {
1426
1427            public int compare( final Object o1, final Object o2 )
1428            {
1429                return ( (Argument) o1 ).getIndex() -
1430                       ( (Argument) o2 ).getIndex();
1431
1432            }
1433
1434        } );
1435
1436        final Arguments args = new Arguments();
1437        args.setModelVersion( modelVersion );
1438        args.setArguments( (Argument[]) arguments.toArray(
1439            new Argument[ arguments.size() ] ) );
1440
1441        this.transformModelObject( namespace, argumentsElement, args );
1442
1443        return args;
1444    }
1445
1446    private Module transformModule( final String modelVersion,
1447                                    final String namespace, final Element e,
1448                                    final URL documentResource )
1449        throws ParseException
1450    {
1451        NodeList l;
1452        Module module = new Module();
1453        module.setModelVersion( modelVersion );
1454        module.setName( e.getAttributeNS( namespace, "name" ) );
1455        module.setVersion( e.getAttributeNS( namespace, "version" ) );
1456        if ( e.hasAttributeNS( namespace, "description" ) )
1457        {
1458            final String txt = e.getAttributeNS( namespace, "description" );
1459            module.setDescription( txt );
1460        }
1461
1462        l = e.getElementsByTagNameNS( namespace, "specifications" );
1463
1464        if ( l != null && l.getLength() > 0 )
1465        {
1466            for ( int i = l.getLength() - 1; i >= 0; i-- )
1467            {
1468                if ( l.item( i ).getParentNode().equals( e ) )
1469                {
1470                    module.setSpecifications(
1471                        this.transformSpecifications(
1472                        modelVersion, namespace, (Element) l.item( i ) ) );
1473
1474                    break;
1475                }
1476            }
1477        }
1478
1479        l = e.getElementsByTagNameNS( namespace, "properties" );
1480
1481        if ( l != null && l.getLength() > 0 )
1482        {
1483            for ( int i = l.getLength() - 1; i >= 0; i-- )
1484            {
1485                if ( l.item( i ).getParentNode().equals( e ) )
1486                {
1487                    module.setProperties(
1488                        this.transformProperties(
1489                        modelVersion, namespace, (Element) l.item( i ) ) );
1490
1491                    break;
1492                }
1493            }
1494        }
1495
1496        if ( VersionParser.compare( modelVersion, V_1_3 ) >= 0 )
1497        {
1498            l = e.getElementsByTagNameNS( namespace, "messages" );
1499
1500            if ( l != null && l.getLength() > 0 )
1501            {
1502                for ( int i = l.getLength() - 1; i >= 0; i-- )
1503                {
1504                    if ( l.item( i ).getParentNode().equals( e ) )
1505                    {
1506                        module.setMessages(
1507                            this.transformMessages(
1508                            modelVersion, namespace, module,
1509                            (Element) l.item( i ) ) );
1510
1511                        break;
1512                    }
1513                }
1514            }
1515
1516            this.transformModelObject( namespace, e, module );
1517        }
1518
1519        l = e.getElementsByTagNameNS( namespace, "implementations" );
1520
1521        if ( l != null && l.getLength() > 0 )
1522        {
1523            for ( int i = l.getLength() - 1; i >= 0; i-- )
1524            {
1525                if ( l.item( i ).getParentNode().equals( e ) )
1526                {
1527                    module.setImplementations(
1528                        this.transformImplementations(
1529                        modelVersion, namespace, module,
1530                        (Element) l.item( i ) ) );
1531
1532                    break;
1533                }
1534            }
1535        }
1536
1537        if ( this.moduleMap.containsKey( module.getName() ) &&
1538             Beans.isDesignTime() )
1539        {
1540            if ( LOGGER.isLoggable( Level.CONFIG ) )
1541            {
1542                LOGGER.log( Level.CONFIG, DefaultModelBundle.getInstance().
1543                    getIgnoredModuleMessage(
1544                    Locale.getDefault(), documentResource.toExternalForm(),
1545                    ( (URL) this.moduleMap.get( module.getName() ) ).
1546                    toExternalForm() ) );
1547
1548            }
1549
1550            module = null;
1551        }
1552        else
1553        {
1554            this.moduleMap.put( module.getName(), documentResource );
1555        }
1556
1557        return module;
1558    }
1559
1560    private Specification transformSpecificationReference(
1561        final String namespace, final Element e )
1562    {
1563        String version = null;
1564        final String identifier = e.getAttributeNS( namespace, "identifier" );
1565        if ( e.hasAttributeNS( namespace, "version" ) )
1566        {
1567            version = e.getAttributeNS( namespace, "version" );
1568        }
1569
1570        return this.getSpecification( identifier, version );
1571    }
1572
1573    private Specification transformSpecification( final String modelVersion,
1574                                                  final String namespace,
1575                                                  final Element xml )
1576        throws ParseException
1577    {
1578        final Specification spec =
1579            this.transformSpecificationReference( namespace, xml );
1580
1581        spec.setModelVersion( modelVersion );
1582        spec.setVendor( xml.getAttributeNS( namespace, "vendor" ) );
1583        if ( xml.hasAttributeNS( namespace, "description" ) )
1584        {
1585            final String txt = xml.getAttributeNS( namespace, "description" );
1586            spec.setDescription( txt );
1587        }
1588
1589        if ( xml.hasAttributeNS( namespace, "singleton" ) )
1590        {
1591            spec.setSingleton(
1592                Boolean.valueOf( xml.getAttributeNS(
1593                namespace, "singleton" ) ).booleanValue() );
1594
1595        }
1596
1597        if ( VersionParser.compare( modelVersion, V_1_1 ) >= 0 )
1598        {
1599            final String multiplicity =
1600                xml.getAttributeNS( namespace, "multiplicity" );
1601
1602            if ( "one".equalsIgnoreCase( multiplicity ) )
1603            {
1604                spec.setMultiplicity( Specification.MULTIPLICITY_ONE );
1605            }
1606            else if ( "many".equalsIgnoreCase( multiplicity ) )
1607            {
1608                spec.setMultiplicity( Specification.MULTIPLICITY_MANY );
1609            }
1610            else
1611            {
1612                throw new ModelError(
1613                    DefaultModelBundle.getInstance().
1614                    getUnsupportedMultiplicityMessage( Locale.getDefault(),
1615                                                       multiplicity ) );
1616
1617            }
1618        }
1619
1620        if ( VersionParser.compare( modelVersion, V_1_3 ) >= 0 )
1621        {
1622            if ( xml.hasAttributeNS( namespace, "stateless" ) )
1623            {
1624                spec.setStateless( Boolean.valueOf( xml.getAttributeNS(
1625                    namespace, "stateless" ) ).booleanValue() );
1626
1627            }
1628
1629            final String scope = xml.getAttributeNS( namespace, "scope" );
1630            if ( "multiton".equalsIgnoreCase( scope ) )
1631            {
1632                spec.setScope( Specification.SCOPE_MULTITON );
1633            }
1634            else if ( "context".equalsIgnoreCase( scope ) )
1635            {
1636                spec.setScope( Specification.SCOPE_CONTEXT );
1637            }
1638            else if ( "singleton".equalsIgnoreCase( scope ) )
1639            {
1640                spec.setScope( Specification.SCOPE_SINGLETON );
1641            }
1642            else
1643            {
1644                throw new ModelError(
1645                    DefaultModelBundle.getInstance().
1646                    getUnsupportedScopeMessage( Locale.getDefault(),
1647                                                spec.getIdentifier(), scope ) );
1648
1649            }
1650
1651            final NodeList l =
1652                xml.getElementsByTagNameNS( namespace, "properties" );
1653
1654            if ( l != null && l.getLength() > 0 )
1655            {
1656                for ( int i = l.getLength() - 1; i >= 0; i-- )
1657                {
1658                    if ( l.item( i ).getParentNode().equals( xml ) )
1659                    {
1660                        spec.setProperties(
1661                            this.transformProperties(
1662                            modelVersion, namespace, (Element) l.item( i ) ) );
1663
1664                        break;
1665                    }
1666                }
1667            }
1668
1669            this.transformModelObject( namespace, xml, spec );
1670        }
1671
1672        return spec;
1673    }
1674
1675    private Implementation transformImplementation( final String modelVersion,
1676                                                    final String namespace,
1677                                                    final Module module,
1678                                                    final Element xml )
1679        throws ParseException
1680    {
1681        final String identifier = xml.getAttributeNS( namespace, "identifier" );
1682        final Implementation impl = this.getImplementation( identifier );
1683        impl.setModelVersion( modelVersion );
1684        impl.setModuleName( module.getName() );
1685        impl.setName( xml.getAttributeNS( namespace, "name" ) );
1686        impl.setVendor( xml.getAttributeNS( namespace, "vendor" ) );
1687        impl.setVersion( xml.getAttributeNS( namespace, "version" ) );
1688
1689        if ( xml.hasAttributeNS( namespace, "parent" ) )
1690        {
1691            this.impl2parent.put( impl.getIdentifier(),
1692                                  xml.getAttributeNS( namespace, "parent" ) );
1693
1694        }
1695
1696        NodeList l = xml.getElementsByTagNameNS( namespace, "dependencies" );
1697
1698        if ( l != null && l.getLength() > 0 )
1699        {
1700            for ( int i = l.getLength() - 1; i >= 0; i-- )
1701            {
1702                if ( l.item( i ).getParentNode().equals( xml ) )
1703                {
1704                    impl.setDependencies(
1705                        this.transformDependencies(
1706                        modelVersion, namespace, identifier,
1707                        (Element) l.item( i ) ) );
1708
1709                    break;
1710                }
1711            }
1712        }
1713
1714        final String specificationsName =
1715            VersionParser.compare( modelVersion, V_1_3 ) >= 0
1716            ? "specifications"
1717            : "implementedSpecifications";
1718
1719        l = xml.getElementsByTagNameNS( namespace, specificationsName );
1720        if ( l != null && l.getLength() > 0 )
1721        {
1722            for ( int i = l.getLength() - 1; i >= 0; i-- )
1723            {
1724                if ( l.item( i ).getParentNode().equals( xml ) )
1725                {
1726                    if ( VersionParser.compare( modelVersion, V_1_3 ) >= 0 )
1727                    {
1728                        impl.setImplementedSpecifications(
1729                            this.transformSpecifications(
1730                            modelVersion, namespace, (Element) l.item( i ) ) );
1731
1732                        break;
1733                    }
1734                    else
1735                    {
1736                        final Set set = new HashSet();
1737                        final NodeList deprecated =
1738                            ( (Element) l.item( i ) ).getElementsByTagNameNS(
1739                            namespace, "implementedSpecification" );
1740
1741                        if ( deprecated != null && deprecated.getLength() > 0 )
1742                        {
1743                            for ( int d = deprecated.getLength() - 1; d >= 0;
1744                                  d-- )
1745                            {
1746                                if ( deprecated.item( d ).getParentNode().
1747                                    equals( l.item( i ) ) )
1748                                {
1749                                    set.add(
1750                                        this.transformSpecificationReference(
1751                                        namespace,
1752                                        (Element) deprecated.item( d ) ) );
1753
1754                                }
1755                            }
1756                        }
1757
1758                        final Specifications specs = new Specifications();
1759                        specs.setModelVersion( modelVersion );
1760                        specs.setSpecifications(
1761                            (Specification[]) set.toArray(
1762                            new Specification[ set.size() ] ) );
1763
1764                        impl.setImplementedSpecifications( specs );
1765                        break;
1766                    }
1767                }
1768            }
1769        }
1770
1771        l = xml.getElementsByTagNameNS( namespace, "properties" );
1772
1773        if ( l != null && l.getLength() > 0 )
1774        {
1775            for ( int i = l.getLength() - 1; i >= 0; i-- )
1776            {
1777                if ( l.item( i ).getParentNode().equals( xml ) )
1778                {
1779                    impl.setProperties(
1780                        this.transformProperties( modelVersion, namespace,
1781                                                  (Element) l.item( i ) ) );
1782
1783                    break;
1784                }
1785            }
1786        }
1787
1788        if ( VersionParser.compare( modelVersion, V_1_1 ) >= 0 &&
1789             xml.hasAttributeNS( namespace, "final" ) )
1790        {
1791            impl.setFinal( Boolean.valueOf( xml.getAttributeNS( namespace,
1792                                                                "final" ) ).
1793                booleanValue() );
1794
1795        }
1796
1797        if ( VersionParser.compare( modelVersion, V_1_3 ) >= 0 )
1798        {
1799            l = xml.getElementsByTagNameNS( namespace, "messages" );
1800
1801            if ( l != null && l.getLength() > 0 )
1802            {
1803                for ( int i = l.getLength() - 1; i >= 0; i-- )
1804                {
1805                    if ( l.item( i ).getParentNode().equals( xml ) )
1806                    {
1807                        impl.setMessages( this.transformMessages(
1808                            modelVersion, namespace, module,
1809                            (Element) l.item( i ) ) );
1810
1811                        break;
1812                    }
1813                }
1814            }
1815
1816            this.transformModelObject( namespace, xml, impl );
1817        }
1818
1819        return impl;
1820    }
1821
1822    private Property transformProperty( final String modelVersion,
1823                                        final String namespace,
1824                                        final Element xml )
1825        throws ParseException
1826    {
1827        final Property prop = new Property();
1828        prop.setModelVersion( modelVersion );
1829        prop.setName( xml.getAttributeNS( namespace, "name" ) );
1830
1831        if ( xml.hasAttributeNS( namespace, "api" ) )
1832        {
1833            final String api = xml.getAttributeNS( namespace, "api" );
1834            prop.setApi( Boolean.valueOf( api ).booleanValue() );
1835        }
1836
1837        final String type = xml.hasAttributeNS( namespace, "type" )
1838                            ? xml.getAttributeNS( namespace, "type" )
1839                            : null;
1840
1841        final String value = xml.hasAttributeNS( namespace, "value" )
1842                             ? xml.getAttributeNS( namespace, "value" )
1843                             : null;
1844
1845        if ( type == null )
1846        {
1847            throw new ModelError(
1848                DefaultModelBundle.getInstance().
1849                getMissingPropertyTypeMessage(
1850                Locale.getDefault(), prop.getName(),
1851                xml.getParentNode().getParentNode().getLocalName() ) );
1852
1853        }
1854
1855        this.updatePropertyValue( type, value, prop );
1856
1857        if ( VersionParser.compare( modelVersion, V_1_3 ) >= 0 )
1858        {
1859            this.transformModelObject( namespace, xml, prop );
1860        }
1861
1862        return prop;
1863    }
1864
1865    private Dependency transformDependency(
1866        final String modelVersion, final String namespace,
1867        final String implementationIdentifier, final Element xml )
1868        throws ParseException
1869    {
1870        final Dependency dep = new Dependency();
1871        dep.setModelVersion( modelVersion );
1872        dep.setName( xml.getAttributeNS( namespace, "name" ) );
1873
1874        if ( xml.hasAttributeNS( namespace, "bound" ) )
1875        {
1876            dep.setBound( Boolean.valueOf( xml.getAttributeNS( namespace,
1877                                                               "bound" ) ).
1878                booleanValue() );
1879
1880        }
1881
1882        if ( xml.hasAttributeNS( namespace, "implementationName" ) )
1883        {
1884            final String name = xml.getAttributeNS( namespace,
1885                                                    "implementationName" );
1886
1887            final String key = implementationIdentifier + '/' +
1888                               dep.getName();
1889
1890            this.dependencies.put( key, name );
1891        }
1892
1893        final String specificationIdentifier =
1894            VersionParser.compare( modelVersion, V_1_3 ) < 0
1895            ? "specificationIdentifier"
1896            : "identifier";
1897
1898        final String identifier = xml.getAttributeNS( namespace,
1899                                                      specificationIdentifier );
1900
1901        if ( VersionParser.compare( modelVersion, V_1_3 ) < 0 )
1902        {
1903            dep.setSpecification( this.getSpecification( identifier, null ) );
1904        }
1905        else
1906        {
1907            String version = null;
1908            if ( xml.hasAttributeNS( namespace, "version" ) )
1909            {
1910                version = xml.getAttributeNS( namespace, "version" );
1911            }
1912
1913            dep.setSpecification( this.getSpecification( identifier,
1914                                                         version ) );
1915
1916        }
1917
1918        final NodeList l =
1919            xml.getElementsByTagNameNS( namespace, "properties" );
1920
1921        if ( l != null && l.getLength() > 0 )
1922        {
1923            for ( int i = l.getLength() - 1; i >= 0; i-- )
1924            {
1925                if ( l.item( i ).getParentNode().equals( xml ) )
1926                {
1927                    dep.setProperties(
1928                        this.transformProperties(
1929                        modelVersion, namespace, (Element) l.item( i ) ) );
1930
1931                    break;
1932                }
1933            }
1934        }
1935
1936        if ( VersionParser.compare( modelVersion, V_1_3 ) >= 0 )
1937        {
1938            this.transformModelObject( namespace, xml, dep );
1939        }
1940
1941        return dep;
1942    }
1943
1944    private Text transformText( final String namespace,
1945                                final Element textsElement )
1946    {
1947        final Text text = new Text();
1948        final String defaultLanguage =
1949            textsElement.getAttributeNS( namespace, "defaultLanguage" );
1950
1951        final NodeList l =
1952            textsElement.getElementsByTagNameNS( namespace, "text" );
1953
1954        if ( l != null && l.getLength() > 0 )
1955        {
1956            for ( int i = l.getLength() - 1; i >= 0; i-- )
1957            {
1958                final Element textElement = (Element) l.item( i );
1959                if ( textElement.getParentNode().equals( textsElement ) )
1960                {
1961                    final String language =
1962                        textElement.getAttributeNS( namespace, "language" );
1963
1964                    final String value =
1965                        textElement.getFirstChild().getNodeValue();
1966
1967                    final Locale locale = new Locale( language.toLowerCase() );
1968                    text.setValue( locale, value );
1969
1970                    if ( language.equalsIgnoreCase( defaultLanguage ) )
1971                    {
1972                        text.setValue( value );
1973                    }
1974                }
1975            }
1976        }
1977
1978        return text;
1979    }
1980
1981    private void transformModelObject( final String namespace,
1982                                       final Element objectElement,
1983                                       final ModelObject modelObject )
1984    {
1985        final NodeList l = objectElement.getElementsByTagNameNS(
1986            namespace, "documentation" );
1987
1988        if ( l != null && l.getLength() > 0 )
1989        {
1990            for ( int i = l.getLength() - 1; i >= 0; i-- )
1991            {
1992                if ( l.item( i ).getParentNode().equals( objectElement ) )
1993                {
1994                    modelObject.setDocumentation(
1995                        this.transformText( namespace,
1996                                            (Element) l.item( i ) ) );
1997
1998                    break;
1999                }
2000            }
2001        }
2002    }
2003
2004    private Message transformMessage( final String modelVersion,
2005                                      final String moduleName,
2006                                      final String namespace,
2007                                      final Element xml )
2008    {
2009        final Message msg = new Message();
2010        msg.setModelVersion( modelVersion );
2011        msg.setModuleName( moduleName );
2012        msg.setName( xml.getAttributeNS( namespace, "name" ) );
2013
2014        NodeList l = xml.getElementsByTagNameNS( namespace, "template" );
2015
2016        if ( l != null && l.getLength() > 0 )
2017        {
2018            for ( int i = l.getLength() - 1; i >= 0; i-- )
2019            {
2020                if ( l.item( i ).getParentNode().equals( xml ) )
2021                {
2022                    final Text template = this.transformText(
2023                        namespace, (Element) l.item( i ) );
2024
2025                    final Locale[] locales = template.getLocales();
2026                    for ( int t = locales.length - 1; t >= 0; t-- )
2027                    {
2028                        final String text = template.getValue( locales[t] );
2029                        try
2030                        {
2031                            new MessageFormat( text );
2032                        }
2033                        catch ( final IllegalArgumentException e )
2034                        {
2035                            final ModelError modelError = new ModelError(
2036                                DefaultModelBundle.getInstance().
2037                                getIllegalTemplateMessage( Locale.getDefault(),
2038                                                           text, msg.getName(),
2039                                                           e.getMessage() ) );
2040
2041                            modelError.initCause( e );
2042                            throw modelError;
2043                        }
2044                    }
2045
2046                    if ( template.getValue() != null )
2047                    {
2048                        try
2049                        {
2050                            new MessageFormat( template.getValue() );
2051                        }
2052                        catch ( final IllegalArgumentException e )
2053                        {
2054                            final ModelError modelError = new ModelError(
2055                                DefaultModelBundle.getInstance().
2056                                getIllegalTemplateMessage( Locale.getDefault(),
2057                                                           template.getValue(),
2058                                                           msg.getName(),
2059                                                           e.getMessage() ) );
2060
2061                            modelError.initCause( e );
2062                            throw modelError;
2063                        }
2064                    }
2065
2066                    msg.setTemplate( template );
2067                    break;
2068                }
2069            }
2070        }
2071
2072        l = xml.getElementsByTagNameNS( namespace, "arguments" );
2073
2074        if ( l != null && l.getLength() > 0 )
2075        {
2076            for ( int i = l.getLength() - 1; i >= 0; i-- )
2077            {
2078                if ( l.item( i ).getParentNode().equals( xml ) )
2079                {
2080                    msg.setArguments(
2081                        this.transformArguments(
2082                        modelVersion, namespace, (Element) l.item( i ) ) );
2083
2084                    break;
2085                }
2086            }
2087        }
2088
2089        this.transformModelObject( namespace, xml, msg );
2090
2091        return msg;
2092    }
2093
2094    private Argument transformArgument( final String modelVersion,
2095                                        final String namespace,
2096                                        final Element xml )
2097    {
2098        final Argument arg = new Argument();
2099        arg.setModelVersion( modelVersion );
2100        arg.setName( xml.getAttributeNS( namespace, "name" ) );
2101        arg.setIndex( Integer.valueOf( xml.getAttributeNS( namespace,
2102                                                           "index" ) ).
2103            intValue() );
2104
2105        final String type = xml.getAttributeNS( namespace, "type" );
2106        if ( type.equalsIgnoreCase( "number" ) )
2107        {
2108            arg.setType( Argument.TYPE_NUMBER );
2109        }
2110        else if ( type.equalsIgnoreCase( "date" ) )
2111        {
2112            arg.setType( Argument.TYPE_DATE );
2113        }
2114        else if ( type.equalsIgnoreCase( "time" ) )
2115        {
2116            arg.setType( Argument.TYPE_TIME );
2117        }
2118        else if ( type.equalsIgnoreCase( "text" ) )
2119        {
2120            arg.setType( Argument.TYPE_TEXT );
2121        }
2122        else
2123        {
2124            throw new ModelError( DefaultModelBundle.getInstance().
2125                getUnsupportedArgumentTypeMessage(
2126                Locale.getDefault(), type ) );
2127
2128        }
2129
2130        this.transformModelObject( namespace, xml, arg );
2131
2132        return arg;
2133    }
2134
2135    /**
2136     * Gets the {@code Module} holding platform specifications.
2137     *
2138     * @return the {@code Module} holding platform specifications.
2139     */
2140    protected Module getPlatformModule()
2141    {
2142        if ( this.platformModule == null )
2143        {
2144            final String description =
2145                DefaultModelBundle.getInstance().
2146                getPlatformModuleDescriptionMessage( Locale.getDefault() );
2147
2148            this.platformModule = new Module();
2149            this.platformModule.getDocumentation().setValue( description );
2150            this.platformModule.setName( PLATFORM_MODULE_NAME );
2151            this.platformModule.setVersion( PLATFORM_MODULE_VERSION );
2152
2153            if ( LOGGER.isLoggable( Level.CONFIG ) )
2154            {
2155                LOGGER.log( Level.CONFIG, DefaultModelBundle.getInstance().
2156                    getAddedPlatformModuleMessage(
2157                    Locale.getDefault(),
2158                    this.platformModule.getDocumentation().getValue() ) );
2159            }
2160        }
2161
2162        return this.platformModule;
2163    }
2164
2165    /**
2166     * Checks an identifier to identify a valid platform specification.
2167     *
2168     * @param identifier the identifier to check.
2169     *
2170     * @return {@code true} if {@code identifier} identifies a valid platform
2171     * specification; {@code false} else.
2172     *
2173     * @throws NullPointerException if {@code identifier} is {@code null}.
2174     */
2175    protected boolean isPlatformSpecification( final String identifier )
2176    {
2177        return this.addPlatformSpecification( identifier, false );
2178    }
2179
2180    /**
2181     * Checks an identifier to identify a valid platform specification and
2182     * optionally adds a new {@code Specification} instance to the platform
2183     * module.
2184     *
2185     * @param identifier the identifier to check.
2186     * @param add {@code true} to add a new {@code Specification} instance to
2187     * the platform module; {@code false} to not update the platform module.
2188     *
2189     * @return {@code true} if {@code identifier} is an identifier of a valid
2190     * platform specification; {@code false} else.
2191     */
2192    private boolean addPlatformSpecification( final String identifier,
2193                                              final boolean add )
2194    {
2195        if ( identifier == null )
2196        {
2197            throw new NullPointerException( "identifier" );
2198        }
2199
2200        boolean validPlatformSpec = false;
2201
2202        try
2203        {
2204            final Class platformClass = ClassLoaderFactory.loadClass(
2205                this.getClass(), identifier );
2206
2207            if ( Modifier.isPublic( platformClass.getModifiers() ) )
2208            {
2209                if ( platformClass.getPackage() != null )
2210                {
2211                    final String specVersion = platformClass.getPackage().
2212                        getSpecificationVersion();
2213
2214                    String specVendor = platformClass.getPackage().
2215                        getSpecificationVendor();
2216
2217                    if ( specVendor == null )
2218                    {
2219                        specVendor = DefaultModelBundle.getInstance().
2220                            getUnknownVendorMessage( Locale.getDefault() );
2221
2222                    }
2223
2224                    if ( specVersion != null )
2225                    {
2226                        validPlatformSpec = true;
2227
2228                        if ( add )
2229                        {
2230                            // Add to the platform module.
2231                            try
2232                            {
2233                                final Specification platformSpec =
2234                                    this.getSpecification( identifier,
2235                                                           specVersion );
2236
2237                                platformSpec.setModelVersion( MODEL_VERSION );
2238                                platformSpec.setDocumentation(
2239                                    this.getPlatformModule().
2240                                    getDocumentation() );
2241
2242                                platformSpec.setIdentifier( identifier );
2243                                platformSpec.setModuleName(
2244                                    this.getPlatformModule().getName() );
2245
2246                                platformSpec.setMultiplicity(
2247                                    Specification.MULTIPLICITY_MANY );
2248
2249                                platformSpec.setScope(
2250                                    Specification.SCOPE_MULTITON );
2251
2252                                platformSpec.setVendor( specVendor );
2253
2254                                final Specification[] platformSpecs =
2255                                    this.getPlatformModule().
2256                                    getSpecifications().getSpecifications();
2257
2258                                final Specification[] newPlatformSpecs =
2259                                    new Specification[ platformSpecs.length + 1 ];
2260
2261                                System.arraycopy( platformSpecs, 0,
2262                                                  newPlatformSpecs, 0,
2263                                                  platformSpecs.length );
2264
2265                                newPlatformSpecs[platformSpecs.length] =
2266                                    platformSpec;
2267
2268                                final Specifications specs =
2269                                    new Specifications();
2270
2271                                specs.setSpecifications( newPlatformSpecs );
2272                                this.getPlatformModule().
2273                                    setSpecifications( specs );
2274
2275                                if ( LOGGER.isLoggable( Level.FINE ) )
2276                                {
2277                                    LOGGER.log( Level.FINE,
2278                                                DefaultModelBundle.getInstance().
2279                                        getAddedPlatformSpecificationMessage(
2280                                        Locale.getDefault(), identifier,
2281                                        specVersion,
2282                                        specVendor ) );
2283
2284                                }
2285
2286                                this.addDefaultImplementation( platformClass,
2287                                                               platformSpec );
2288
2289                            }
2290                            catch ( final DuplicateSpecificationException e )
2291                            {
2292                                // Specification already created.
2293                            }
2294                        }
2295                    }
2296                    else
2297                    {
2298                        LOGGER.log( Level.WARNING,
2299                                    DefaultModelBundle.getInstance().
2300                            getNoVersionAvailableMessage( Locale.getDefault(),
2301                                                          identifier ) );
2302
2303                        validPlatformSpec = false;
2304                    }
2305                }
2306                else
2307                {
2308                    LOGGER.log( Level.WARNING, DefaultModelBundle.getInstance().
2309                        getNoPackageAvailableMessage( Locale.getDefault(),
2310                                                      identifier ) );
2311
2312                    validPlatformSpec = false;
2313                }
2314            }
2315            else
2316            {
2317                LOGGER.log( Level.WARNING, DefaultModelBundle.getInstance().
2318                    getNotPublicMessage( Locale.getDefault(), identifier ) );
2319
2320                validPlatformSpec = false;
2321            }
2322        }
2323        catch ( final ClassNotFoundException ex )
2324        {
2325            LOGGER.log( Level.WARNING, DefaultModelBundle.getInstance().
2326                getClassNotFoundMessage( Locale.getDefault(), identifier ) );
2327
2328            validPlatformSpec = false;
2329        }
2330
2331        return validPlatformSpec;
2332    }
2333
2334    private void addDefaultImplementation( final Class platformClass,
2335                                           final Specification platformSpec )
2336    {
2337        try
2338        {
2339            final Method accessor = platformClass.getDeclaredMethod(
2340                "getDefault", new Class[ 0 ] );
2341
2342            if ( Modifier.isStatic( accessor.getModifiers() ) &&
2343                 accessor.getReturnType() == platformClass )
2344            {
2345                boolean overwritten = false;
2346
2347                // Check for an overwritten implementation.
2348                for ( int i = this.modules.getImplementations().size() - 1;
2349                      i >= 0; i-- )
2350                {
2351                    final Implementation resourceImpl =
2352                        this.modules.getImplementations().
2353                        getImplementation( i );
2354
2355                    if ( !"default".equals( resourceImpl.getName() ) )
2356                    {
2357                        continue;
2358                    }
2359
2360                    for ( int s = resourceImpl.getImplementedSpecifications().
2361                        size() - 1; s >= 0; s-- )
2362                    {
2363                        final Specification implemented =
2364                            resourceImpl.getImplementedSpecifications().
2365                            getSpecification( s );
2366
2367                        if ( implemented.getIdentifier().equals(
2368                            platformSpec.getIdentifier() ) )
2369                        {
2370                            if ( LOGGER.isLoggable( Level.FINE ) )
2371                            {
2372                                LOGGER.log( Level.FINE,
2373                                            DefaultModelBundle.getInstance().
2374                                    getOverwrittenDefaultImplementationMessage(
2375                                    Locale.getDefault(),
2376                                    platformSpec.getIdentifier(),
2377                                    platformSpec.getIdentifier(),
2378                                    resourceImpl.getIdentifier() ) );
2379
2380                            }
2381
2382                            overwritten = true;
2383                            break;
2384                        }
2385                    }
2386                }
2387
2388                final String implVersion = platformClass.getPackage().
2389                    getImplementationVersion();
2390
2391                String implVendor = platformClass.getPackage().
2392                    getImplementationVendor();
2393
2394                if ( implVendor == null )
2395                {
2396                    implVendor = DefaultModelBundle.getInstance().
2397                        getUnknownVendorMessage( Locale.getDefault() );
2398
2399                }
2400
2401                if ( implVersion != null && !overwritten )
2402                {
2403                    final Implementation defaultImpl = this.getImplementation(
2404                        platformClass.getName() );
2405
2406                    defaultImpl.setModelVersion( MODEL_VERSION );
2407                    defaultImpl.setVersion( implVersion );
2408                    defaultImpl.setVendor( implVendor );
2409                    defaultImpl.setDocumentation(
2410                        platformSpec.getDocumentation() );
2411
2412                    defaultImpl.setFinal( true );
2413                    defaultImpl.setModuleName( PLATFORM_MODULE_NAME );
2414                    defaultImpl.setName( "default" );
2415
2416                    final Specifications implemented = new Specifications();
2417                    implemented.setSpecifications( new Specification[]
2418                        {
2419                            platformSpec
2420                        } );
2421
2422                    defaultImpl.setImplementedSpecifications( implemented );
2423
2424                    final Implementation[] current = this.getPlatformModule().
2425                        getImplementations().getImplementations();
2426
2427                    final Implementation[] tmp =
2428                        new Implementation[ current.length + 1 ];
2429
2430                    System.arraycopy( current, 0, tmp, 0, current.length );
2431                    tmp[current.length] = defaultImpl;
2432
2433                    final Implementations impls = new Implementations();
2434                    impls.setImplementations( tmp );
2435
2436                    this.getPlatformModule().setImplementations( impls );
2437
2438                    if ( LOGGER.isLoggable( Level.FINE ) )
2439                    {
2440                        LOGGER.log( Level.FINE, DefaultModelBundle.getInstance().
2441                            getAddedDefaultImplementationMessage(
2442                            Locale.getDefault(), defaultImpl.getIdentifier(),
2443                            platformSpec.getIdentifier(), implVersion,
2444                            implVendor ) );
2445
2446                    }
2447
2448                }
2449            }
2450        }
2451        catch ( final NoSuchMethodException e )
2452        {
2453            // No static accessor.
2454        }
2455    }
2456
2457    /**
2458     * Updates the value of a property.
2459     *
2460     * @param typeName the name of the type of {@code property}.
2461     * @param value the value of {@code property}.
2462     * @param property the property to update.
2463     */
2464    private void updatePropertyValue( final String typeName,
2465                                      final String value,
2466                                      final Property property )
2467    {
2468        final Class objectType;
2469
2470        if ( typeName.equals( Boolean.TYPE.getName() ) )
2471        {
2472            property.setType( Boolean.TYPE );
2473            objectType = Boolean.class;
2474        }
2475        else if ( typeName.equals( Byte.TYPE.getName() ) )
2476        {
2477            property.setType( Byte.TYPE );
2478            objectType = Byte.class;
2479        }
2480        else if ( typeName.equals( Character.TYPE.getName() ) )
2481        {
2482            property.setType( Character.TYPE );
2483            objectType = Character.class;
2484        }
2485        else if ( typeName.equals( Double.TYPE.getName() ) )
2486        {
2487            property.setType( Double.TYPE );
2488            objectType = Double.class;
2489        }
2490        else if ( typeName.equals( Float.TYPE.getName() ) )
2491        {
2492            property.setType( Float.TYPE );
2493            objectType = Float.class;
2494        }
2495        else if ( typeName.equals( Integer.TYPE.getName() ) )
2496        {
2497            property.setType( Integer.TYPE );
2498            objectType = Integer.class;
2499        }
2500        else if ( typeName.equals( Long.TYPE.getName() ) )
2501        {
2502            property.setType( Long.TYPE );
2503            objectType = Long.class;
2504        }
2505        else if ( typeName.equals( Short.TYPE.getName() ) )
2506        {
2507            property.setType( Short.TYPE );
2508            objectType = Short.class;
2509        }
2510        else if ( typeName.equals( Boolean.class.getName() ) )
2511        {
2512            property.setType( Boolean.class );
2513            objectType = Boolean.class;
2514        }
2515        else if ( typeName.equals( Byte.class.getName() ) )
2516        {
2517            property.setType( Byte.class );
2518            objectType = Byte.class;
2519        }
2520        else if ( typeName.equals( Character.class.getName() ) )
2521        {
2522            property.setType( Character.class );
2523            objectType = Character.class;
2524        }
2525        else if ( typeName.equals( Double.class.getName() ) )
2526        {
2527            property.setType( Double.class );
2528            objectType = Double.class;
2529        }
2530        else if ( typeName.equals( Float.class.getName() ) )
2531        {
2532            property.setType( Float.class );
2533            objectType = Float.class;
2534        }
2535        else if ( typeName.equals( Integer.class.getName() ) )
2536        {
2537            property.setType( Integer.class );
2538            objectType = Integer.class;
2539        }
2540        else if ( typeName.equals( Long.class.getName() ) )
2541        {
2542            property.setType( Long.class );
2543            objectType = Long.class;
2544        }
2545        else if ( typeName.equals( Short.class.getName() ) )
2546        {
2547            property.setType( Short.class );
2548            objectType = Short.class;
2549        }
2550        else if ( typeName.equals( String.class.getName() ) )
2551        {
2552            property.setType( String.class );
2553            objectType = String.class;
2554        }
2555        else
2556        {
2557            throw new ModelError( DefaultModelBundle.getInstance().
2558                getUnsupportedPropertyTypeMessage( Locale.getDefault(),
2559                                                   typeName ) );
2560
2561        }
2562
2563        if ( value != null )
2564        {
2565            try
2566            {
2567                // Special handling for class Character which does not provide
2568                // a constructor taking a string.
2569                final Constructor ctor;
2570                final Object arg;
2571
2572                if ( objectType == Character.class )
2573                {
2574                    ctor = objectType.getDeclaredConstructor( CTOR_ARGS_CHAR );
2575                    arg = new Character( value.charAt( 0 ) );
2576                }
2577                else
2578                {
2579                    ctor = objectType.getDeclaredConstructor( CTOR_ARGS_STRING );
2580                    arg = value;
2581                }
2582
2583                property.setValue( ctor.newInstance( new Object[]
2584                    {
2585                        arg
2586                    } ) );
2587
2588            }
2589            catch ( final SecurityException e )
2590            {
2591                throw new ModelError( e );
2592            }
2593            catch ( final NoSuchMethodException e )
2594            {
2595                throw new ModelError( e );
2596            }
2597            catch ( final InvocationTargetException e )
2598            {
2599                final Throwable targetException = e.getTargetException();
2600
2601                if ( targetException instanceof Error )
2602                {
2603                    throw (Error) targetException;
2604                }
2605                else if ( targetException instanceof RuntimeException )
2606                {
2607                    throw (RuntimeException) targetException;
2608                }
2609                else
2610                {
2611                    throw new ModelError( targetException == null
2612                                          ? e
2613                                          : targetException );
2614
2615                }
2616            }
2617            catch ( final IllegalAccessException e )
2618            {
2619                throw new ModelError( e );
2620            }
2621            catch ( final InstantiationException e )
2622            {
2623                throw new ModelError( e );
2624            }
2625        }
2626        else
2627        {
2628            property.setValue( value );
2629        }
2630    }
2631
2632    //------------------------------------------------------------DefaultModel--
2633}