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