001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/framework/xml/schema/XSDocument.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.List; 047 048 import org.deegree.datatypes.QualifiedName; 049 import org.deegree.framework.log.ILogger; 050 import org.deegree.framework.log.LoggerFactory; 051 import org.deegree.framework.xml.XMLFragment; 052 import org.deegree.framework.xml.XMLParsingException; 053 import org.deegree.framework.xml.XMLTools; 054 import org.w3c.dom.Element; 055 import org.w3c.dom.Node; 056 057 /** 058 * Parser for XML schema documents. 059 * 060 * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a> 061 * @author <a href="mailto:deshmukh@lat-lon.de">Anup Deshmukh </a> 062 * @author last edited by: $Author: apoth $ 063 * 064 * @version $Revision: 9339 $, $Date: 2007-12-27 13:31:52 +0100 (Do, 27 Dez 2007) $ 065 */ 066 public class XSDocument extends XMLFragment { 067 068 private static final long serialVersionUID = 4371672452129797159L; 069 070 private URI targetNamespace; 071 072 private final ILogger LOG = LoggerFactory.getLogger( XSDocument.class ); 073 074 /** 075 * Returns the class representation of the underlying schema document. 076 * 077 * @return class representation of the underlying schema document 078 * @throws XMLParsingException 079 * @throws XMLSchemaException 080 */ 081 public XMLSchema parseXMLSchema() 082 throws XMLParsingException, XMLSchemaException { 083 SimpleTypeDeclaration[] simpleTypes = extractSimpleTypeDeclarations(); 084 ComplexTypeDeclaration[] complexTypes = extractComplexTypeDeclarations(); 085 ElementDeclaration[] elementDeclarations = extractElementDeclarations(); 086 return new XMLSchema( getTargetNamespace(), simpleTypes, complexTypes, elementDeclarations ); 087 } 088 089 /** 090 * Returns the target namespace of the underlying schema document. 091 * 092 * @return target namespace of the underlying schema document 093 * @throws XMLParsingException 094 */ 095 public synchronized URI getTargetNamespace() 096 throws XMLParsingException { 097 if ( this.targetNamespace == null ) { 098 this.targetNamespace = XMLTools.getRequiredNodeAsURI( this.getRootElement(), "@targetNamespace", nsContext ); 099 } 100 return this.targetNamespace; 101 } 102 103 /** 104 * Extracts all global (top-level) simple type declarations from the underlying schema document. 105 * 106 * @return all global (top-level) simple type declarations 107 * @throws XMLParsingException 108 * if the document is not a valid XML Schema document or does not match the 109 * limitations of this class 110 */ 111 public SimpleTypeDeclaration[] extractSimpleTypeDeclarations() 112 throws XMLParsingException { 113 List simpleTypeElements = XMLTools.getNodes( this.getRootElement(), getFullName( "simpleType" ), nsContext ); 114 LOG.logDebug( "Found " + simpleTypeElements.size() + " simple type declarations." ); 115 SimpleTypeDeclaration[] simpleTypeDeclarations = new SimpleTypeDeclaration[simpleTypeElements.size()]; 116 for ( int i = 0; i < simpleTypeDeclarations.length; i++ ) { 117 simpleTypeDeclarations[i] = parseSimpleTypeDeclaration( (Element) simpleTypeElements.get( i ) ); 118 } 119 return simpleTypeDeclarations; 120 } 121 122 /** 123 * Extracts all global (top-level) complex type declarations from the underlying schema 124 * document. 125 * 126 * @return all global (top-level) complex type declarations 127 * @throws XMLParsingException 128 * if the document is not a valid XML Schema document or does not match the 129 * limitations of this class 130 */ 131 public ComplexTypeDeclaration[] extractComplexTypeDeclarations() 132 throws XMLParsingException { 133 List complexTypeElements = XMLTools.getNodes( this.getRootElement(), getFullName( "complexType" ), nsContext ); 134 LOG.logDebug( "Found " + complexTypeElements.size() + " complex type declarations." ); 135 ComplexTypeDeclaration[] complexTypeDeclarations = new ComplexTypeDeclaration[complexTypeElements.size()]; 136 for ( int i = 0; i < complexTypeDeclarations.length; i++ ) { 137 complexTypeDeclarations[i] = parseComplexTypeDeclaration( (Element) complexTypeElements.get( i ) ); 138 } 139 return complexTypeDeclarations; 140 } 141 142 /** 143 * Extracts all global (top-level) element declarations from the underlying schema document. 144 * 145 * @return all global (top-level) element declarations 146 * @throws XMLParsingException 147 * if the document is not a valid XML Schema document or does not match the 148 * limitations of this class 149 */ 150 public ElementDeclaration[] extractElementDeclarations() 151 throws XMLParsingException { 152 List complexTypeElements = XMLTools.getNodes( this.getRootElement(), getFullName( "element" ), nsContext ); 153 LOG.logDebug( "Found " + complexTypeElements.size() + " element declarations." ); 154 ElementDeclaration[] elementDeclarations = new ElementDeclaration[complexTypeElements.size()]; 155 for ( int i = 0; i < elementDeclarations.length; i++ ) { 156 elementDeclarations[i] = parseElementDeclaration( (Element) complexTypeElements.get( i ) ); 157 } 158 return elementDeclarations; 159 } 160 161 /** 162 * Returns the root element of the complex type declaration for the given name. 163 * 164 * @param name 165 * the name of the complex type declaration to look up (w/o namespace) 166 * @return the root element of the complex type declaration or null, if the requested complex 167 * type is not declared 168 */ 169 public Element getComplexTypeDeclaration( String name ) { 170 String xPath = getFullName( "complexType[name=\"]" ) + name + "\"]"; 171 Element element = null; 172 try { 173 element = (Element) XMLTools.getNode( getRootElement(), xPath, nsContext ); 174 } catch ( XMLParsingException e ) { 175 // happens if requested complex type is not declared 176 } 177 return element; 178 } 179 180 /** 181 * Returns the root element of the element declaration for the given name. 182 * 183 * @param name 184 * the name of the element declaration to look up (w/o namespace) 185 * @return the root element of the element declaration or null, if the requested element is not 186 * declared 187 */ 188 public Element getElementDeclaration( String name ) { 189 String xPath = getFullName( "element[name=\"]" ) + name + "\"]"; 190 Element element = null; 191 try { 192 element = (Element) XMLTools.getNode( getRootElement(), xPath, nsContext ); 193 } catch ( XMLParsingException e ) { 194 // happens if requested element is not declared 195 } 196 return element; 197 } 198 199 /** 200 * Parses the given <code>Element</code> as an 'xs:element' declaration. 201 * 202 * @param element 203 * 'xs:element' declaration to be parsed 204 * @return object representation of the declaration 205 * @throws XMLParsingException 206 * if the document is not a valid XML Schema document or does not match the 207 * limitations of this class 208 */ 209 protected ElementDeclaration parseElementDeclaration( Element element ) 210 throws XMLParsingException { 211 212 QualifiedName name = new QualifiedName( XMLTools.getRequiredNodeAsString( element, "@name", nsContext ), 213 getTargetNamespace() ); 214 215 if ( name.getLocalName().length() == 0 ) { 216 String msg = "Error in schema document. Empty name (\"\") in element declaration " + "found."; 217 throw new XMLSchemaException( msg ); 218 } 219 220 LOG.logDebug( "Parsing element declaration '" + name + "'." ); 221 222 boolean isAbstract = XMLTools.getNodeAsBoolean( element, "@abstract", nsContext, false ); 223 224 TypeReference typeReference = null; 225 Node typeNode = XMLTools.getNode( element, 226 "@type|xs:simpleType/xs:restriction/@base|xs:simpleType/xs:extension/@base", 227 nsContext ); 228 if ( typeNode != null ) { 229 typeReference = new TypeReference( parseQualifiedName( typeNode ) ); 230 } else { 231 // inline type declaration 232 Element elem = (Element) XMLTools.getRequiredNode( element, getFullName( "complexType" ), nsContext ); 233 TypeDeclaration type = parseComplexTypeDeclaration( elem ); 234 typeReference = new TypeReference( type ); 235 } 236 237 int minOccurs = XMLTools.getNodeAsInt( element, "@minOccurs", nsContext, 1 ); 238 int maxOccurs = -1; 239 String maxOccursString = XMLTools.getNodeAsString( element, "@maxOccurs", nsContext, "1" ); 240 if ( !"unbounded".equals( maxOccursString ) ) { 241 try { 242 maxOccurs = Integer.parseInt( maxOccursString ); 243 } catch ( NumberFormatException e ) { 244 throw new XMLParsingException( "Invalid value ('" + maxOccursString + "') in 'maxOccurs' attribute. " 245 + "Must be a valid integer value or 'unbounded'." ); 246 } 247 } 248 249 QualifiedName substitutionGroup = null; 250 Node substitutionGroupNode = XMLTools.getNode( element, "@substitutionGroup", nsContext ); 251 if ( substitutionGroupNode != null ) { 252 substitutionGroup = parseQualifiedName( substitutionGroupNode ); 253 } 254 255 return new ElementDeclaration( name, isAbstract, typeReference, minOccurs, maxOccurs, substitutionGroup ); 256 } 257 258 /** 259 * Parses the given <code>Element</code> as an 'xs:simpleType' declaration. 260 * <p> 261 * The following limitations apply: 262 * <ul> 263 * <li>the type must be defined using 'restriction' (of a basic xsd type)</li> 264 * <li>the content model (enumeration, ...) is not evaluated</li> 265 * </ul> 266 * </p> 267 * 268 * @param element 269 * 'xs:simpleType' declaration to be parsed 270 * @return object representation of the declaration 271 * @throws XMLParsingException 272 * if the document is not a valid XML Schema document or does not match the 273 * limitations of this class 274 */ 275 protected SimpleTypeDeclaration parseSimpleTypeDeclaration( Element element ) 276 throws XMLParsingException { 277 278 QualifiedName name = null; 279 String localName = XMLTools.getNodeAsString( element, "@name", nsContext, null ); 280 if ( localName != null ) { 281 name = new QualifiedName( localName, getTargetNamespace() ); 282 if ( localName.length() == 0 ) { 283 String msg = "Error in schema document. Empty name (\"\") in simpleType " + "declaration found."; 284 throw new XMLSchemaException( msg ); 285 } 286 } 287 288 LOG.logDebug( "Parsing simple type declaration '" + name + "'." ); 289 290 Node restrictionBaseNode = XMLTools.getRequiredNode( element, getFullName( "restriction/@base" ), nsContext ); 291 TypeReference restrictionBase = new TypeReference( parseQualifiedName( restrictionBaseNode ) ); 292 293 return new SimpleTypeDeclaration( name, restrictionBase ); 294 } 295 296 /** 297 * Parses the given <code>Element</code> as an 'xs:complexType' declaration. 298 * 299 * @param element 300 * 'xs:complexType' declaration to be parsed 301 * @return object representation of the declaration 302 * @throws XMLParsingException 303 * if the document is not a valid XML Schema document or does not match the 304 * limitations of this class 305 */ 306 protected ComplexTypeDeclaration parseComplexTypeDeclaration( Element element ) 307 throws XMLParsingException { 308 309 QualifiedName name = null; 310 String localName = XMLTools.getNodeAsString( element, "@name", nsContext, null ); 311 if ( localName != null ) { 312 name = new QualifiedName( localName, getTargetNamespace() ); 313 if ( localName.length() == 0 ) { 314 String msg = "Error in schema document. Empty name (\"\") for complexType " + "declaration found."; 315 throw new XMLSchemaException( msg ); 316 } 317 } 318 LOG.logDebug( "Parsing complex type declaration '" + name + "'." ); 319 320 List subElementList = null; 321 TypeReference extensionBase = null; 322 Node extensionBaseNode = XMLTools.getNode( element, getFullName( "complexContent/" ) 323 + getFullName( "extension/@base" ), nsContext ); 324 if ( extensionBaseNode != null ) { 325 extensionBase = new TypeReference( parseQualifiedName( extensionBaseNode ) ); 326 subElementList = XMLTools.getNodes( element, getFullName( "complexContent/" ) + getFullName( "extension/" ) 327 + getFullName( "sequence/" ) + getFullName( "element" ), 328 nsContext ); 329 } else { 330 subElementList = XMLTools.getRequiredNodes( element, getFullName( "sequence/" ) + getFullName( "element" ), 331 nsContext ); 332 } 333 334 ElementDeclaration[] subElements = new ElementDeclaration[subElementList.size()]; 335 for ( int i = 0; i < subElements.length; i++ ) { 336 Element subElement = (Element) subElementList.get( i ); 337 subElements[i] = parseElementDeclaration( subElement ); 338 } 339 340 return new ComplexTypeDeclaration( name, extensionBase, subElements ); 341 } 342 343 /** 344 * Prepends the prefix of the RootElement to the given local name. 345 * <p> 346 * If the prefix of the RootElement is empty, "xs:" is prepended. 347 * 348 * @param localName 349 * to this the prefix will be prepended 350 * @return prefix + localName 351 */ 352 protected String getFullName( String localName ) { 353 String ret; 354 Element root = this.getRootElement(); 355 String prefix = root.getPrefix(); 356 357 if ( prefix != null && prefix.length() > 0 ) { 358 URI uri = nsContext.getURI( prefix ); 359 if ( null == uri ) { 360 String nsUri = root.lookupNamespaceURI( prefix ); 361 try { 362 nsContext.addNamespace( prefix, new URI( nsUri ) ); // synchronized ??? 363 } catch ( Exception exc ) { 364 LOG.logError( "failed to add namespace: " + nsUri, exc ); 365 } 366 } 367 ret = prefix + ':' + localName; 368 } else { 369 // fallback 370 ret = "xs:" + localName; 371 } 372 return ret; 373 } 374 }