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 }