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