//$Header: /home/deegree/jail/deegreerepository/deegree/src/org/deegree/framework/xml/schema/XSDocument.java,v 1.16 2006/08/29 19:54:14 poth Exp $
/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2006 by:
EXSE, Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/deegree/
lat/lon GmbH
http://www.lat-lon.de
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact:
Andreas Poth
lat/lon GmbH
Aennchenstraße 19
53177 Bonn
Germany
E-Mail: poth@lat-lon.de
Prof. Dr. Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: greve@giub.uni-bonn.de
---------------------------------------------------------------------------*/
package org.deegree.framework.xml.schema;
import java.net.URI;
import java.util.List;
import org.deegree.datatypes.QualifiedName;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.log.LoggerFactory;
import org.deegree.framework.xml.XMLFragment;
import org.deegree.framework.xml.XMLParsingException;
import org.deegree.framework.xml.XMLTools;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* Parser for XML Schema documents.
*
* @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
* @author <a href="mailto:deshmukh@lat-lon.de">Anup Deshmukh </a>
* @author last edited by: $Author: poth $
*
* @version $Revision: 1.16 $, $Date: 2006/08/29 19:54:14 $
*/
public class XSDocument extends XMLFragment {
private static final long serialVersionUID = 4371672452129797159L;
private URI targetNamespace;
private final ILogger LOG = LoggerFactory.getLogger( XSDocument.class );
/**
* Returns the class representation of the underlying schema document.
*
* @return class representation of the underlying schema document
* @throws XMLParsingException
* @throws XMLSchemaException
*/
public XMLSchema parseXMLSchema()
throws XMLParsingException, XMLSchemaException {
SimpleTypeDeclaration[] simpleTypes = extractSimpleTypeDeclarations();
ComplexTypeDeclaration[] complexTypes = extractComplexTypeDeclarations();
ElementDeclaration[] elementDeclarations = extractElementDeclarations();
return new XMLSchema( getTargetNamespace(), simpleTypes, complexTypes, elementDeclarations );
}
/**
* Returns the target namespace of the underlying schema document.
*
* @return target namespace of the underlying schema document
* @throws XMLParsingException
*/
public URI getTargetNamespace()
throws XMLParsingException {
if ( this.targetNamespace == null ) {
this.targetNamespace = XMLTools.getRequiredNodeAsURI( this.getRootElement(),
"@targetNamespace", nsContext );
}
return this.targetNamespace;
}
/**
* Extracts all global (top-level) simple type declarations from the underlying schema document.
*
* @return all global (top-level) simple type declarations
* @throws XMLParsingException
* if the document is not a valid XML Schema document or does not match the
* limitations of this class
*/
public SimpleTypeDeclaration[] extractSimpleTypeDeclarations()
throws XMLParsingException {
List simpleTypeElements = XMLTools.getNodes( this.getRootElement(),
getFullName( "simpleType" ), nsContext );
LOG.logDebug( "Found " + simpleTypeElements.size() + " simple type declarations." );
SimpleTypeDeclaration[] simpleTypeDeclarations = new SimpleTypeDeclaration[simpleTypeElements.size()];
for ( int i = 0; i < simpleTypeDeclarations.length; i++ ) {
simpleTypeDeclarations[i] = parseSimpleTypeDeclaration( (Element) simpleTypeElements.get( i ) );
}
return simpleTypeDeclarations;
}
/**
* Extracts all global (top-level) complex type declarations from the underlying schema
* document.
*
* @return all global (top-level) complex type declarations
* @throws XMLParsingException
* if the document is not a valid XML Schema document or does not match the
* limitations of this class
*/
public ComplexTypeDeclaration[] extractComplexTypeDeclarations()
throws XMLParsingException {
List complexTypeElements = XMLTools.getNodes( this.getRootElement(),
getFullName( "complexType" ), nsContext );
LOG.logDebug( "Found " + complexTypeElements.size() + " complex type declarations." );
ComplexTypeDeclaration[] complexTypeDeclarations = new ComplexTypeDeclaration[complexTypeElements.size()];
for ( int i = 0; i < complexTypeDeclarations.length; i++ ) {
complexTypeDeclarations[i] = parseComplexTypeDeclaration( (Element) complexTypeElements.get( i ) );
}
return complexTypeDeclarations;
}
/**
* Extracts all global (top-level) element declarations from the underlying schema document.
*
* @return all global (top-level) element declarations
* @throws XMLParsingException
* if the document is not a valid XML Schema document or does not match the
* limitations of this class
*/
public ElementDeclaration[] extractElementDeclarations()
throws XMLParsingException {
List complexTypeElements = XMLTools.getNodes( this.getRootElement(),
getFullName( "element" ), nsContext );
LOG.logDebug( "Found " + complexTypeElements.size() + " element declarations." );
ElementDeclaration[] elementDeclarations = new ElementDeclaration[complexTypeElements.size()];
for ( int i = 0; i < elementDeclarations.length; i++ ) {
elementDeclarations[i] = parseElementDeclaration( (Element) complexTypeElements.get( i ) );
}
return elementDeclarations;
}
/**
* Returns the root element of the complex type declaration for the given name.
*
* @param name
* the name of the complex type declaration to look up (w/o namespace)
* @return the root element of the complex type declaration or null, if the requested complex
* type is not declared
*/
public Element getComplexTypeDeclaration( String name ) {
String xPath = getFullName( "complexType[name=\"]" ) + name + "\"]";
Element element = null;
try {
element = (Element) XMLTools.getNode( getRootElement(), xPath, nsContext );
} catch ( XMLParsingException e ) {
// happens if requested complex type is not declared
}
return element;
}
/**
* Returns the root element of the element declaration for the given name.
*
* @param name
* the name of the element declaration to look up (w/ot namespace)
* @return the root element of the element declaration or null, if the requested element is not
* declared
*/
public Element getElementDeclaration( String name ) {
String xPath = getFullName( "element[name=\"]" ) + name + "\"]";
Element element = null;
try {
element = (Element) XMLTools.getNode( getRootElement(), xPath, nsContext );
} catch ( XMLParsingException e ) {
// happens if requested element is not declared
}
return element;
}
/**
* Parses the given <code>Element</code> as an 'xs:element' declaration.
*
* @param element
* 'xs:element' declaration to be parsed
* @return object representation of the declaration
* @throws XMLParsingException
* if the document is not a valid XML Schema document or does not match the
* limitations of this class
*/
protected ElementDeclaration parseElementDeclaration( Element element )
throws XMLParsingException {
QualifiedName name = new QualifiedName( XMLTools.getRequiredNodeAsString( element, "@name",
nsContext ),
getTargetNamespace() );
if ( name.getLocalName().length() == 0 ) {
String msg = "Error in schema document. Empty name (\"\") in element declaration "
+ "found.";
throw new XMLSchemaException( msg );
}
LOG.logDebug( "Parsing element declaration '" + name + "'." );
boolean isAbstract = XMLTools.getNodeAsBoolean( element, "@abstract", nsContext, false );
TypeReference typeReference = null;
Node typeNode = XMLTools.getNode( element, "@type", nsContext );
if ( typeNode != null ) {
typeReference = new TypeReference( parseQualifiedName( typeNode ) );
} else {
// inline type declaration
Element elem = (Element) XMLTools.getRequiredNode( element,
getFullName( "complexType" ),
nsContext );
TypeDeclaration type = parseComplexTypeDeclaration( elem );
typeReference = new TypeReference( type );
}
int minOccurs = XMLTools.getNodeAsInt( element, "@minOccurs", nsContext, 1 );
int maxOccurs = -1;
String maxOccursString = XMLTools.getNodeAsString( element, "@maxOccurs", nsContext, "1" );
if ( !"unbounded".equals( maxOccursString ) ) {
try {
maxOccurs = Integer.parseInt( maxOccursString );
} catch ( NumberFormatException e ) {
throw new XMLParsingException( "Invalid value ('" + maxOccursString
+ "') in 'maxOccurs' attribute. "
+ "Must be a valid integer value or 'unbounded'." );
}
}
QualifiedName substitutionGroup = null;
Node substitutionGroupNode = XMLTools.getNode( element, "@substitutionGroup", nsContext );
if ( substitutionGroupNode != null ) {
substitutionGroup = parseQualifiedName( substitutionGroupNode );
}
return new ElementDeclaration( name, isAbstract, typeReference, minOccurs, maxOccurs,
substitutionGroup );
}
/**
* Parses the given <code>Element</code> as an 'xs:simpleType' declaration.
* <p>
* The following limitations apply:
* <ul>
* <li>the type must be defined using 'restriction' (of a basic xsd type)</li>
* <li>the content model (enumeration, ...) is not evaluated</li>
* </ul>
* </p>
*
* @param element
* 'xs:simpleType' declaration to be parsed
* @return object representation of the declaration
* @throws XMLParsingException
* if the document is not a valid XML Schema document or does not match the
* limitations of this class
*/
protected SimpleTypeDeclaration parseSimpleTypeDeclaration( Element element )
throws XMLParsingException {
QualifiedName name = null;
String localName = XMLTools.getNodeAsString( element, "@name", nsContext, null );
if ( localName != null ) {
name = new QualifiedName( localName, getTargetNamespace() );
if ( localName.length() == 0 ) {
String msg = "Error in schema document. Empty name (\"\") in simpleType "
+ "declaration found.";
throw new XMLSchemaException( msg );
}
}
LOG.logDebug( "Parsing simple type declaration '" + name + "'." );
Node restrictionBaseNode = XMLTools.getRequiredNode( element,
getFullName( "restriction/@base" ),
nsContext );
TypeReference restrictionBase = new TypeReference( parseQualifiedName( restrictionBaseNode ) );
return new SimpleTypeDeclaration( name, restrictionBase );
}
/**
* Parses the given <code>Element</code> as an 'xs:complexType' declaration.
*
* @param element
* 'xs:complexType' declaration to be parsed
* @return object representation of the declaration
* @throws XMLParsingException
* if the document is not a valid XML Schema document or does not match the
* limitations of this class
*/
protected ComplexTypeDeclaration parseComplexTypeDeclaration( Element element )
throws XMLParsingException {
QualifiedName name = null;
String localName = XMLTools.getNodeAsString( element, "@name", nsContext, null );
if ( localName != null ) {
name = new QualifiedName( localName, getTargetNamespace() );
if ( localName.length() == 0 ) {
String msg = "Error in schema document. Empty name (\"\") for complexType "
+ "declaration found.";
throw new XMLSchemaException( msg );
}
}
LOG.logDebug( "Parsing complex type declaration '" + name + "'." );
List subElementList = null;
TypeReference extensionBase = null;
Node extensionBaseNode = XMLTools.getNode( element, getFullName( "complexContent/" )
+ getFullName( "extension/@base" ),
nsContext );
if ( extensionBaseNode != null ) {
extensionBase = new TypeReference( parseQualifiedName( extensionBaseNode ) );
subElementList = XMLTools.getNodes( element, getFullName( "complexContent/" )
+ getFullName( "extension/" )
+ getFullName( "sequence/" )
+ getFullName( "element" ), nsContext );
} else {
subElementList = XMLTools.getRequiredNodes( element, getFullName( "sequence/" )
+ getFullName( "element" ),
nsContext );
}
ElementDeclaration[] subElements = new ElementDeclaration[subElementList.size()];
for ( int i = 0; i < subElements.length; i++ ) {
Element subElement = (Element) subElementList.get( i );
subElements[i] = parseElementDeclaration( subElement );
}
return new ComplexTypeDeclaration( name, extensionBase, subElements );
}
/**
* Prepends the prefix of the RootElement to the given local name.
* <p>
* If the prefix of the RootElement is empty, "xs:" is prepended.
*
* @param localName to this the prefix will be prepended
* @return prefix + localName
*/
protected String getFullName( String localName ) {
String ret;
Element root = this.getRootElement();
String prefix = root.getPrefix();
if ( prefix != null && prefix.length() > 0 ) {
URI uri = nsContext.getURI( prefix );
if ( null == uri ) {
String nsUri = root.lookupNamespaceURI( prefix );
try {
nsContext.addNamespace( prefix, new URI( nsUri ) ); // synchronized ???
} catch ( Exception exc ) {
LOG.logError( "failed to add namespace: " + nsUri, exc );
}
}
ret = prefix + ':' + localName;
} else {
// fallback
ret = "xs:" + localName;
}
return ret;
}
}
/* ********************************************************************
Changes to this class. What the people have been up to:
$Log: XSDocument.java,v $
Revision 1.16 2006/08/29 19:54:14 poth
footer corrected
Revision 1.15 2006/08/29 15:51:06 mschneider
Fixed handling of empty element and type names.
Revision 1.14 2006/08/29 14:47:09 mschneider
Added handling of empty element and type names
Revision 1.13 2006/08/24 06:39:17 poth
File header corrected
Revision 1.12 2006/08/22 18:14:42 mschneider
Refactored due to cleanup of org.deegree.io.datastore.schema package.
Revision 1.11 2006/07/23 10:27:13 poth
generalizing determination of schema prefix
Revision 1.10 2006/07/12 14:46:16 poth
comment footer added
********************************************************************** */