001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_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    }