//$Header$ /*---------------- 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.HashMap; import java.util.Iterator; import java.util.Map; import org.deegree.datatypes.QualifiedName; import org.deegree.framework.log.ILogger; import org.deegree.framework.log.LoggerFactory; /** * Represents an XML Schema document. * * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a> * @author last edited by: $Author$ * * @version $Revision$, $Date$ */ public class XMLSchema { private final static ILogger LOG = LoggerFactory.getLogger( XMLSchema.class ); private URI targetNamespace; // keys: QualifiedName (element names), values: ElementDeclaration private Map<QualifiedName, ElementDeclaration> elementMap = new HashMap<QualifiedName, ElementDeclaration>(); // keys: QualifiedName (type names), values: TypeDeclaration private Map<QualifiedName, TypeDeclaration> typeMap = new HashMap<QualifiedName, TypeDeclaration>(); // keys: QualifiedName (type names), values: ComplexTypeDeclaration private Map<QualifiedName, ComplexTypeDeclaration> complexTypeMap = new HashMap<QualifiedName, ComplexTypeDeclaration>(); // keys: QualifiedName (type names), values: SimpleTypeDeclaration private Map<QualifiedName, SimpleTypeDeclaration> simpleTypeMap = new HashMap<QualifiedName, SimpleTypeDeclaration>(); /** * Creates a new <code>XMLSchema</code> instance from the given parameters. * * @param targetNamespace * @param simpleTypes * @param complexTypes * @param elementDeclarations * @throws XMLSchemaException */ public XMLSchema( URI targetNamespace, SimpleTypeDeclaration[] simpleTypes, ComplexTypeDeclaration[] complexTypes, ElementDeclaration[] elementDeclarations ) throws XMLSchemaException { this.targetNamespace = targetNamespace; for ( int i = 0; i < elementDeclarations.length; i++ ) { elementMap.put( elementDeclarations[i].getName(), elementDeclarations[i] ); } for ( int i = 0; i < simpleTypes.length; i++ ) { simpleTypeMap.put( simpleTypes[i].getName(), simpleTypes[i] ); typeMap.put( simpleTypes[i].getName(), simpleTypes[i] ); } for ( int i = 0; i < complexTypes.length; i++ ) { complexTypeMap.put( complexTypes[i].getName(), complexTypes[i] ); typeMap.put( complexTypes[i].getName(), complexTypes[i] ); } resolveReferences(); } /** * Returns the target namespace of the schema document. * * @return the target namespace */ public URI getTargetNamespace() { return this.targetNamespace; } /** * Returns all <code>ElementDeclaration</code>s that are defined in the schema. * * @return all ElementDeclarations that are defined in the schema */ public ElementDeclaration[] getElementDeclarations() { return this.elementMap.values().toArray( new ElementDeclaration[this.elementMap.size()] ); } /** * Returns all <code>SimpleTypeDeclaration</code>s that are defined in the schema. * * @return all SimpleTypeDeclarations that are defined in the schema */ public SimpleTypeDeclaration[] getSimpleTypeDeclarations() { return this.simpleTypeMap.values().toArray( new SimpleTypeDeclaration[this.simpleTypeMap.size()] ); } /** * Returns all <code>ComplexTypeDeclaration</code>s that are defined in the schema. * * @return all ComplexTypeDeclarations that are defined in the schema */ public ComplexTypeDeclaration[] getComplexTypeDeclarations() { return this.complexTypeMap.values().toArray( new ComplexTypeDeclaration[this.complexTypeMap.size()] ); } /** * Looks up the <code>ElementDeclaration</code> for the given <code>QualifiedName</code>. * * @param qName * the QualifiedName to look up * @return the ElementDeclaration, if an element with the given name is defined in the schema, * null otherwise */ public ElementDeclaration getElementDeclaration( QualifiedName qName ) { return this.elementMap.get( qName ); } /** * Looks up the <code>TypeDeclaration</code> for the given <code>QualifiedName</code>. * * @param qName * the QualifiedName to look up * @return the TypeDeclaration, if a type with the given name is defined in the schema, null * otherwise */ public TypeDeclaration getTypeDeclaration( QualifiedName qName ) { return this.typeMap.get( qName ); } /** * Looks up the <code>SimpleTypeDeclaration</code> for the given <code>QualifiedName</code>. * * @param qName * the QualifiedName to look up * @return the SimpleTypeDeclaration, if a simple type with the given name is defined in the * schema, null otherwise */ public SimpleTypeDeclaration getSimpleTypeDeclaration( QualifiedName qName ) { return this.simpleTypeMap.get( qName ); } /** * Looks up the <code>ComplexTypeDeclaration</code> for the given <code>QualifiedName</code>. * * @param qName * the QualifiedName to look up * @return the ComplexTypeDeclaration, if a complex type with the given name is defined in the * schema, null otherwise */ public ComplexTypeDeclaration getComplexTypeDeclaration( QualifiedName qName ) { return this.complexTypeMap.get( qName ); } /** * Looks up the <code>ElementDeclaration</code> for the given local name (without namespace). * * @param name * the (unqualified) name to look up * @return the ElementDeclaration, if an element with the given name is defined in the schema, * null otherwise */ public ElementDeclaration getElementDeclaration( String name ) { return getElementDeclaration( new QualifiedName( name, this.targetNamespace ) ); } /** * Looks up the <code>TypeDeclaration</code> for the given local name (without namespace). * * @param name * the (unqualified) name to look up * @return the TypeDeclaration, if a type with the given name is defined in the schema, null * otherwise */ public TypeDeclaration getTypeDeclaration( String name ) { return getTypeDeclaration( new QualifiedName( name, this.targetNamespace ) ); } /** * Looks up the <code>SimpleTypeDeclaration</code> for the given local name (without * namespace). * * @param name * the (unqualified) name to look up * @return the SimpleTypeDeclaration, if a simple type with the given name is defined in the * schema, null otherwise */ public SimpleTypeDeclaration getSimpleTypeDeclaration( String name ) { return getSimpleTypeDeclaration( new QualifiedName( name, this.targetNamespace ) ); } /** * Looks up the <code>ComplexTypeDeclaration</code> for the given local name (without * namespace). * * @param name * the (unqualified) name to look up * @return the ComplexTypeDeclaration, if a complex type with the given name is defined in the * schema, null otherwise */ public ComplexTypeDeclaration getComplexTypeDeclaration( String name ) { return getComplexTypeDeclaration( new QualifiedName( name, this.targetNamespace ) ); } private void resolveReferences() throws UnresolvableReferenceException { LOG.logDebug( "Resolving references for namespace '" + this.targetNamespace + "'." ); Iterator iter = elementMap.values().iterator(); while ( iter.hasNext() ) { resolveReferences( (ElementDeclaration) iter.next() ); } iter = typeMap.values().iterator(); while ( iter.hasNext() ) { resolveReferences( (TypeDeclaration) iter.next() ); } } private void resolveReferences( ElementDeclaration element ) throws UnresolvableReferenceException { LOG.logDebug( "Resolving references in element declaration '" + element.getName().getLocalName() + "'." ); ElementReference substitutionGroup = element.getSubstitutionGroup(); if ( substitutionGroup != null ) { resolveElement( substitutionGroup ); } TypeReference typeReference = element.getType(); resolveType( typeReference ); } private void resolveReferences( TypeDeclaration typeDeclaration ) throws UnresolvableReferenceException { LOG.logDebug( "Resolving references in type declaration '" + typeDeclaration.getName().getLocalName() + "'." ); if ( typeDeclaration instanceof SimpleTypeDeclaration ) { LOG.logDebug( "SimpleType." ); SimpleTypeDeclaration simpleType = (SimpleTypeDeclaration) typeDeclaration; TypeReference typeReference = simpleType.getRestrictionBaseType(); if ( typeReference != null ) { LOG.logDebug( "restriction base='" + typeReference.getName() + "'" ); try { resolveType( typeReference ); } catch ( XMLSchemaException e ) { throw new UndefinedXSDTypeException( "Declaration of type '" + typeDeclaration.getName() + "' derives type '" + typeReference.getName() + "' which is not a defined simple type." ); } } } else { LOG.logDebug( "ComplexType." ); ComplexTypeDeclaration complexType = (ComplexTypeDeclaration) typeDeclaration; TypeReference typeReference = complexType.getExtensionBaseType(); if ( typeReference != null ) { LOG.logDebug( "extension base='" + typeReference.getName() + "'" ); try { resolveType( typeReference ); } catch ( XMLSchemaException e ) { throw new UndefinedXSDTypeException( "Declaration of type '" + typeDeclaration.getName() + "' derives type '" + typeReference.getName() + "' which is not a defined complex type." ); } } ElementDeclaration[] elements = complexType.getExplicitElements(); for ( int i = 0; i < elements.length; i++ ) { resolveReferences( elements[i] ); } } } private void resolveElement( ElementReference elementReference ) throws UndefinedElementException { if ( !elementReference.isResolved() ) { LOG.logDebug( "Resolving reference to element '" + elementReference.getName().getLocalName() + "'." ); if ( elementReference.getName().isInNamespace( this.targetNamespace ) ) { ElementDeclaration element = elementMap.get( elementReference.getName() ); if ( element == null ) { LOG.logDebug( "Cannot be resolved!" ); throw new UndefinedElementException( "Element '" + elementReference.getName() + "' is not defined." ); } LOG.logDebug( "OK." ); elementReference.resolve( element ); } else { LOG.logDebug( "Skipped (not in target namespace)." ); elementReference.resolve(); } } } private void resolveType( TypeReference typeReference ) throws UnresolvableReferenceException { if ( !typeReference.isResolved() ) { if ( typeReference.isAnonymous() ) { LOG.logDebug( "Inline type..." ); // type is defined inline TypeDeclaration type = typeReference.getTypeDeclaration(); typeReference.resolve(); if ( type instanceof ComplexTypeDeclaration ) { ComplexTypeDeclaration complexType = (ComplexTypeDeclaration) type; ElementDeclaration[] subElements = complexType.getExplicitElements(); for ( int i = 0; i < subElements.length; i++ ) { resolveReferences( subElements[i] ); } } } else { LOG.logDebug( "Resolving reference to type: '" + typeReference.getName() + "'..." ); if ( typeReference.getName().isInNamespace( this.targetNamespace ) ) { TypeDeclaration type = typeMap.get( typeReference.getName() ); if ( type == null ) { LOG.logDebug( "Cannot be resolved!" ); throw new UndefinedXSDTypeException( "Type '" + typeReference.getName() + "' is not a defined type." ); } LOG.logDebug( "OK." ); typeReference.resolve( type ); } else { LOG.logDebug( "Skipped (not in target / schema namespace)." ); } } } } /** * Returns a string representation of the object. * * @return a string representation of the object */ public String toString() { StringBuffer sb = new StringBuffer( "XML Schema targetNamespace='" ); sb.append( targetNamespace ); sb.append( "'\n" ); sb.append( "\n*** " ); sb.append( elementMap.size() ); sb.append( " global element declarations ***\n" ); Iterator elementIter = elementMap.values().iterator(); while ( elementIter.hasNext() ) { ElementDeclaration element = (ElementDeclaration) elementIter.next(); sb.append( element.toString( "" ) ); } sb.append( "\n*** " ); sb.append( simpleTypeMap.size() ); sb.append( " global simple type declarations ***\n" ); Iterator simpleTypeIter = simpleTypeMap.values().iterator(); while ( simpleTypeIter.hasNext() ) { SimpleTypeDeclaration type = (SimpleTypeDeclaration) simpleTypeIter.next(); sb.append( type.toString( "" ) ); } sb.append( "\n*** " ); sb.append( complexTypeMap.size() ); sb.append( " global complex type declarations ***\n" ); Iterator complexTypeIter = complexTypeMap.values().iterator(); while ( complexTypeIter.hasNext() ) { ComplexTypeDeclaration type = (ComplexTypeDeclaration) complexTypeIter.next(); sb.append( type.toString( "" ) ); } return sb.toString(); } } /* ******************************************************************** Changes to this class. What the people have been up to: $Log$ Revision 1.9 2006/08/29 19:54:14 poth footer corrected Revision 1.8 2006/08/22 18:14:42 mschneider Refactored due to cleanup of org.deegree.io.datastore.schema package. Revision 1.7 2006/07/12 14:46:16 poth comment footer added ********************************************************************** */