    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/framework/xml/schema/XMLSchema.java $
    002    /*----------------------------------------------------------------------------
    003     This file is part of deegree, http://deegree.org/
    004     Copyright (C) 2001-2009 by:
    005       Department of Geography, University of Bonn
    006     and
    007       lat/lon GmbH
    008
    009     This library is free software; you can redistribute it and/or modify it under
    010     the terms of the GNU Lesser General Public License as published by the Free
    011     Software Foundation; either version 2.1 of the License, or (at your option)
    012     any later version.
    013     This library is distributed in the hope that it will be useful, but WITHOUT
    014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
    016     details.
    017     You should have received a copy of the GNU Lesser General Public License
    018     along with this library; if not, write to the Free Software Foundation, Inc.,
    019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    020
    021     Contact information:
    022
    023     lat/lon GmbH
    024     Aennchenstr. 19, 53177 Bonn
    025     Germany
    026     http://lat-lon.de/
    027
    028     Department of Geography, University of Bonn
    029     Prof. Dr. Klaus Greve
    030     Postfach 1147, 53001 Bonn
    031     Germany
    032     http://www.geographie.uni-bonn.de/deegree/
    033
    034     e-mail: info@deegree.org
    035    ----------------------------------------------------------------------------*/
    036    package org.deegree.framework.xml.schema;
    037
    038    import java.net.URI;
    039    import java.util.HashMap;
    040    import java.util.Map;
    041
    042    import org.deegree.datatypes.QualifiedName;
    043    import org.deegree.framework.log.ILogger;
    044    import org.deegree.framework.log.LoggerFactory;
    045
    046    /**
    047     * Represents an XML schema document.
    048     *
    049     * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
    050     * @author last edited by: $Author: mschneider $
    051     *
    052     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
    053     */
    054    public class XMLSchema {
    055
    056        private final static ILogger LOG = LoggerFactory.getLogger( XMLSchema.class );
    057
    058        private URI targetNamespace;
    059
    060        // keys: QualifiedName (element names), values: ElementDeclaration
    061        private Map<QualifiedName, ElementDeclaration> elementMap = new HashMap<QualifiedName, ElementDeclaration>();
    062
    063        // keys: QualifiedName (type names), values: TypeDeclaration
    064        private Map<QualifiedName, TypeDeclaration> typeMap = new HashMap<QualifiedName, TypeDeclaration>();
    065
    066        // keys: QualifiedName (type names), values: ComplexTypeDeclaration
    067        private Map<QualifiedName, ComplexTypeDeclaration> complexTypeMap = new HashMap<QualifiedName, ComplexTypeDeclaration>();
    068
    069        // keys: QualifiedName (type names), values: SimpleTypeDeclaration
    070        private Map<QualifiedName, SimpleTypeDeclaration> simpleTypeMap = new HashMap<QualifiedName, SimpleTypeDeclaration>();
    071
    072        /**
    073         * Creates a new <code>XMLSchema</code> instance from the given parameters.
    074         *
    075         * @param targetNamespace
    076         * @param simpleTypes
    077         * @param complexTypes
    078         * @param elementDeclarations
    079         * @throws XMLSchemaException
    080         */
    081        public XMLSchema( URI targetNamespace, SimpleTypeDeclaration[] simpleTypes,
    082                         ComplexTypeDeclaration[] complexTypes, ElementDeclaration[] elementDeclarations )
    083                                throws XMLSchemaException {
    084            this.targetNamespace = targetNamespace;
    085            for ( int i = 0; i < elementDeclarations.length; i++ ) {
    086                elementMap.put( elementDeclarations[i].getName(), elementDeclarations[i] );
    087            }
    088            for ( int i = 0; i < simpleTypes.length; i++ ) {
    089                simpleTypeMap.put( simpleTypes[i].getName(), simpleTypes[i] );
    090                typeMap.put( simpleTypes[i].getName(), simpleTypes[i] );
    091            }
    092            for ( int i = 0; i < complexTypes.length; i++ ) {
    093                complexTypeMap.put( complexTypes[i].getName(), complexTypes[i] );
    094                typeMap.put( complexTypes[i].getName(), complexTypes[i] );
    095            }
    096            resolveReferences();
    097        }
    098
    099        /**
    100         * Returns the target namespace of the schema document.
    101         *
    102         * @return the target namespace
    103         */
    104        public URI getTargetNamespace() {
    105            return this.targetNamespace;
    106        }
    107
    108        /**
    109         * Returns all <code>ElementDeclaration</code>s that are defined in the schema.
    110         *
    111         * @return all ElementDeclarations that are defined in the schema
    112         */
    113        public ElementDeclaration[] getElementDeclarations() {
    114            return this.elementMap.values().toArray( new ElementDeclaration[this.elementMap.size()] );
    115        }
    116
    117        /**
    118         * Returns all <code>SimpleTypeDeclaration</code>s that are defined in the schema.
    119         *
    120         * @return all SimpleTypeDeclarations that are defined in the schema
    121         */
    122        public SimpleTypeDeclaration[] getSimpleTypeDeclarations() {
    123            return this.simpleTypeMap.values().toArray(
    124                                                        new SimpleTypeDeclaration[this.simpleTypeMap.size()] );
    125        }
    126
    127        /**
    128         * Returns all <code>ComplexTypeDeclaration</code>s that are defined in the schema.
    129         *
    130         * @return all ComplexTypeDeclarations that are defined in the schema
    131         */
    132        public ComplexTypeDeclaration[] getComplexTypeDeclarations() {
    133            return this.complexTypeMap.values().toArray(
    134                                                         new ComplexTypeDeclaration[this.complexTypeMap.size()] );
    135        }
    136
    137        /**
    138         * Looks up the <code>ElementDeclaration</code> for the given <code>QualifiedName</code>.
    139         *
    140         * @param qName
    141         *            the QualifiedName to look up
    142         * @return the ElementDeclaration, if an element with the given name is defined in the schema,
    143         *         null otherwise
    144         */
    145        public ElementDeclaration getElementDeclaration( QualifiedName qName ) {
    146            return this.elementMap.get( qName );
    147        }
    148
    149        /**
    150         * Looks up the <code>TypeDeclaration</code> for the given <code>QualifiedName</code>.
    151         *
    152         * @param qName
    153         *            the QualifiedName to look up
    154         * @return the TypeDeclaration, if a type with the given name is defined in the schema, null
    155         *         otherwise
    156         */
    157        public TypeDeclaration getTypeDeclaration( QualifiedName qName ) {
    158            return this.typeMap.get( qName );
    159        }
    160
    161        /**
    162         * Looks up the <code>SimpleTypeDeclaration</code> for the given <code>QualifiedName</code>.
    163         *
    164         * @param qName
    165         *            the QualifiedName to look up
    166         * @return the SimpleTypeDeclaration, if a simple type with the given name is defined in the
    167         *         schema, null otherwise
    168         */
    169        public SimpleTypeDeclaration getSimpleTypeDeclaration( QualifiedName qName ) {
    170            return this.simpleTypeMap.get( qName );
    171        }
    172
    173        /**
    174         * Looks up the <code>ComplexTypeDeclaration</code> for the given <code>QualifiedName</code>.
    175         *
    176         * @param qName
    177         *            the QualifiedName to look up
    178         * @return the ComplexTypeDeclaration, if a complex type with the given name is defined in the
    179         *         schema, null otherwise
    180         */
    181        public ComplexTypeDeclaration getComplexTypeDeclaration( QualifiedName qName ) {
    182            return this.complexTypeMap.get( qName );
    183        }
    184
    185        /**
    186         * Looks up the <code>ElementDeclaration</code> for the given local name (without namespace).
    187         *
    188         * @param name
    189         *            the (unqualified) name to look up
    190         * @return the ElementDeclaration, if an element with the given name is defined in the schema,
    191         *         null otherwise
    192         */
    193        public ElementDeclaration getElementDeclaration( String name ) {
    194            return getElementDeclaration( new QualifiedName( name, this.targetNamespace ) );
    195        }
    196
    197        /**
    198         * Looks up the <code>TypeDeclaration</code> for the given local name (without namespace).
    199         *
    200         * @param name
    201         *            the (unqualified) name to look up
    202         * @return the TypeDeclaration, if a type with the given name is defined in the schema, null
    203         *         otherwise
    204         */
    205        public TypeDeclaration getTypeDeclaration( String name ) {
    206            return getTypeDeclaration( new QualifiedName( name, this.targetNamespace ) );
    207        }
    208
    209        /**
    210         * Looks up the <code>SimpleTypeDeclaration</code> for the given local name (without
    211         * namespace).
    212         *
    213         * @param name
    214         *            the (unqualified) name to look up
    215         * @return the SimpleTypeDeclaration, if a simple type with the given name is defined in the
    216         *         schema, null otherwise
    217         */
    218        public SimpleTypeDeclaration getSimpleTypeDeclaration( String name ) {
    219            return getSimpleTypeDeclaration( new QualifiedName( name, this.targetNamespace ) );
    220        }
    221
    222        /**
    223         * Looks up the <code>ComplexTypeDeclaration</code> for the given local name (without
    224         * namespace).
    225         *
    226         * @param name
    227         *            the (unqualified) name to look up
    228         * @return the ComplexTypeDeclaration, if a complex type with the given name is defined in the
    229         *         schema, null otherwise
    230         */
    231        public ComplexTypeDeclaration getComplexTypeDeclaration( String name ) {
    232            return getComplexTypeDeclaration( new QualifiedName( name, this.targetNamespace ) );
    233        }
    234
    235        private void resolveReferences()
    236                                throws UnresolvableReferenceException {
    237            LOG.logDebug( "Resolving references for namespace '" + this.targetNamespace + "'." );
    238            for( ElementDeclaration elemDec : elementMap.values() ){
    239                resolveReferences( elemDec );
    240            }
    241            for( TypeDeclaration tDec : typeMap.values() ){
    242                resolveReferences( tDec );
    243            }
    244        }
    245
    246        private void resolveReferences( ElementDeclaration element )
    247                                throws UnresolvableReferenceException {
    248            LOG.logDebug( "Resolving references in element declaration '"
    249                          + element.getName().getLocalName() + "'." );
    250            ElementReference substitutionGroup = element.getSubstitutionGroup();
    251            if ( substitutionGroup != null ) {
    252                resolveElement( substitutionGroup );
    253            }
    254            TypeReference typeReference = element.getType();
    255            resolveType( typeReference );
    256        }
    257
    258        private void resolveReferences( TypeDeclaration typeDeclaration )
    259                                throws UnresolvableReferenceException {
    260            LOG.logDebug( "Resolving references in type declaration '"
    261                          + typeDeclaration.getName().getLocalName() + "'." );
    262            if ( typeDeclaration instanceof SimpleTypeDeclaration ) {
    263                LOG.logDebug( "SimpleType." );
    264                SimpleTypeDeclaration simpleType = (SimpleTypeDeclaration) typeDeclaration;
    265                TypeReference typeReference = simpleType.getRestrictionBaseType();
    266                if ( typeReference != null ) {
    267                    LOG.logDebug( "restriction base='" + typeReference.getName() + "'" );
    268                    try {
    269                        resolveType( typeReference );
    270                    } catch ( XMLSchemaException e ) {
    271                        throw new UndefinedXSDTypeException( "Declaration of type '"
    272                                                             + typeDeclaration.getName()
    273                                                             + "' derives type '"
    274                                                             + typeReference.getName()
    275                                                             + "' which is not a defined simple type." );
    276                    }
    277                }
    278            } else {
    279                LOG.logDebug( "ComplexType." );
    280                ComplexTypeDeclaration complexType = (ComplexTypeDeclaration) typeDeclaration;
    281                TypeReference typeReference = complexType.getExtensionBaseType();
    282                if ( typeReference != null ) {
    283                    LOG.logDebug( "extension base='" + typeReference.getName() + "'" );
    284                    try {
    285                        resolveType( typeReference );
    286                    } catch ( XMLSchemaException e ) {
    287                        throw new UndefinedXSDTypeException( "Declaration of type '"
    288                                                             + typeDeclaration.getName()
    289                                                             + "' derives type '"
    290                                                             + typeReference.getName()
    291                                                             + "' which is not a defined complex type." );
    292                    }
    293                }
    294                ElementDeclaration[] elements = complexType.getExplicitElements();
    295                for ( int i = 0; i < elements.length; i++ ) {
    296                    resolveReferences( elements[i] );
    297                }
    298            }
    299        }
    300
    301        private void resolveElement( ElementReference elementReference )
    302                                throws UndefinedElementException {
    303            if ( !elementReference.isResolved() ) {
    304                LOG.logDebug( "Resolving reference to element '"
    305                              + elementReference.getName().getLocalName() + "'." );
    306                if ( elementReference.getName().isInNamespace( this.targetNamespace ) ) {
    307                    ElementDeclaration element = elementMap.get( elementReference.getName() );
    308                    if ( element == null ) {
    309                        LOG.logDebug( "Cannot be resolved!" );
    310                        throw new UndefinedElementException( "Element '" + elementReference.getName()
    311                                                             + "' is not defined." );
    312                    }
    313                    LOG.logDebug( "OK." );
    314                    elementReference.resolve( element );
    315                } else {
    316                    LOG.logDebug( "Skipped (not in target namespace)." );
    317                    elementReference.resolve();
    318                }
    319            }
    320        }
    321
    322        private void resolveType( TypeReference typeReference )
    323                                throws UnresolvableReferenceException {
    324            if ( !typeReference.isResolved() ) {
    325                if ( typeReference.isAnonymous() ) {
    326                    LOG.logDebug( "Inline type..." );
    327                    // type is defined inline
    328                    TypeDeclaration type = typeReference.getTypeDeclaration();
    329                    typeReference.resolve();
    330                    if ( type instanceof ComplexTypeDeclaration ) {
    331                        ComplexTypeDeclaration complexType = (ComplexTypeDeclaration) type;
    332                        ElementDeclaration[] subElements = complexType.getExplicitElements();
    333                        for ( int i = 0; i < subElements.length; i++ ) {
    334                            resolveReferences( subElements[i] );
    335                        }
    336                    }
    337                } else {
    338                    LOG.logDebug( "Resolving reference to type: '" + typeReference.getName() + "'..." );
    339                    if ( typeReference.getName().isInNamespace( this.targetNamespace ) ) {
    340                        TypeDeclaration type = typeMap.get( typeReference.getName() );
    341                        if ( type == null ) {
    342                            LOG.logDebug( "Cannot be resolved!" );
    343                            throw new UndefinedXSDTypeException( "Type '" + typeReference.getName()
    344                                                                 + "' is not a defined type." );
    345                        }
    346                        LOG.logDebug( "OK." );
    347                        typeReference.resolve( type );
    348                    } else {
    349                        LOG.logDebug( "Skipped (not in target / schema namespace)." );
    350                    }
    351                }
    352            }
    353        }
    354
    355        @Override
    356        public String toString() {
    357            StringBuffer sb = new StringBuffer( "XML Schema targetNamespace='" );
    358            sb.append( targetNamespace );
    359            sb.append( "'\n" );
    360            sb.append( "\n*** " );
    361            sb.append( elementMap.size() );
    362            sb.append( " global element declarations ***\n" );
    363            for( ElementDeclaration elemDec : elementMap.values() ){
    364                sb.append( elemDec.toString( "" ) );
    365            }
    366            sb.append( "\n*** " );
    367            sb.append( simpleTypeMap.size() );
    368            sb.append( " global simple type declarations ***\n" );
    369            for( SimpleTypeDeclaration type : simpleTypeMap.values() ){
    370                sb.append( type.toString( "" ) );
    371            }
    372            sb.append( "\n*** " );
    373            sb.append( complexTypeMap.size() );
    374            sb.append( " global complex type declarations ***\n" );
    375            for( ComplexTypeDeclaration type : complexTypeMap.values() ){
    376                sb.append( type.toString( "" ) );
    377            }
    378            return sb.toString();
    379        }
    380    }