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 }