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 }