001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_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 }