//$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.model.feature.schema;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.deegree.datatypes.QualifiedName;
import org.deegree.datatypes.Types;
import org.deegree.datatypes.UnknownTypeException;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.log.LoggerFactory;
import org.deegree.framework.xml.XMLParsingException;
import org.deegree.framework.xml.schema.ComplexTypeDeclaration;
import org.deegree.framework.xml.schema.ElementDeclaration;
import org.deegree.framework.xml.schema.SimpleTypeDeclaration;
import org.deegree.framework.xml.schema.UndefinedXSDTypeException;
import org.deegree.framework.xml.schema.XMLSchema;
import org.deegree.framework.xml.schema.XMLSchemaException;
import org.deegree.model.crs.UnknownCRSException;
import org.deegree.model.feature.FeatureFactory;
import org.deegree.ogcbase.CommonNamespaces;
/**
* Represents a GML Application Schema document to provide easy access to it's components,
* especially the <code>FeatureType</code> instances defined within.
*
* @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
* @author last edited by: $Author$
*
* @version $Revision$, $Date$
*/
public class GMLSchema extends XMLSchema {
private final static ILogger LOG = LoggerFactory.getLogger( GMLSchema.class );
private static URI XSDNS = CommonNamespaces.XSNS;
private static URI GMLNS = CommonNamespaces.GMLNS;
private static final QualifiedName ABSTRACT_FEATURE = new QualifiedName( "_Feature", GMLNS );
// keys: QualifiedNames (feature type names), values: FeatureTypes
protected Map<QualifiedName, FeatureType> featureTypeMap = new HashMap<QualifiedName, FeatureType>();
// keys: FeatureTypes, values: List (of FeatureTypes)
private Map<FeatureType, List<FeatureType>> substitutionMap = new HashMap<FeatureType, List<FeatureType>>();
/**
* Creates a new <code>GMLSchema</code> instance from the given parameters.
*
* @param targetNamespace
* @param simpleTypes
* @param complexTypes
* @param elementDeclarations
* @throws XMLParsingException
* @throws UnknownCRSException
*/
public GMLSchema( URI targetNamespace, SimpleTypeDeclaration[] simpleTypes,
ComplexTypeDeclaration[] complexTypes, ElementDeclaration[] elementDeclarations )
throws XMLParsingException, UnknownCRSException {
super( targetNamespace, simpleTypes, complexTypes, elementDeclarations );
buildFeatureTypeMap( elementDeclarations );
buildSubstitutionMap( elementDeclarations );
}
// TODO remove this constructor
protected GMLSchema( ElementDeclaration[] elementDeclarations, URI targetNamespace,
SimpleTypeDeclaration[] simpleTypes, ComplexTypeDeclaration[] complexTypes )
throws XMLSchemaException {
super( targetNamespace, simpleTypes, complexTypes, elementDeclarations );
}
/**
* Returns all <code>FeatureTypes</code>s that are defined in the document.
*
* @return all FeatureTypes
*/
public FeatureType[] getFeatureTypes() {
return this.featureTypeMap.values().toArray( new FeatureType[this.featureTypeMap.size()] );
}
/**
* Looks up the <code>FeatureType</code> with the given <code>QualifiedName</code>.
*
* @param qName
* the QualifiedName to look up
* @return the FeatureType, if it is defined in the document, null otherwise
*/
public FeatureType getFeatureType( QualifiedName qName ) {
return this.featureTypeMap.get( qName );
}
/**
* Looks up the <code>FeatureType</code> with the given name.
*
* @param localName
* the name to look up
* @return the FeatureType, if it is defined in the document, null otherwise
*/
public FeatureType getFeatureType( String localName ) {
return getFeatureType( new QualifiedName( localName, getTargetNamespace() ) );
}
/**
* Returns all non-abstract implementations of a given feature type that are defined in this
* schema.
*
* @param featureType
* @return all non-abstract implementations of the feature type
*/
public FeatureType[] getSubstitutions( FeatureType featureType ) {
FeatureType[] substitutions = new FeatureType[0];
List<FeatureType> featureTypeList = this.substitutionMap.get( featureType );
if ( featureTypeList != null ) {
substitutions = featureTypeList.toArray( new FeatureType[featureTypeList.size()] );
}
return substitutions;
}
/**
* Returns whether the specified feature type is a valid substitution for the other specified
* feature type (according to the schema).
*
* @param ft
* @param substitution
* @return true, if it is valid substitution, false otherwise
*/
public boolean isValidSubstitution( FeatureType ft, FeatureType substitution ) {
FeatureType[] substitutions = getSubstitutions( ft );
for ( int i = 0; i < substitutions.length; i++ ) {
if ( substitutions[i].getName().equals( substitution.getName() ) ) {
return true;
}
}
return false;
}
/**
* Initializes the internal feature type map which is used to lookup feature types by name.
*
* @param elementDeclarations
* element declarations to process, only element declarations that are substitutable
* for "gml:_Feature" are considered
* @throws XMLParsingException
* @throws UnknownCRSException
*/
protected void buildFeatureTypeMap( ElementDeclaration[] elementDeclarations )
throws XMLParsingException, UnknownCRSException {
for ( int i = 0; i < elementDeclarations.length; i++ ) {
LOG.logDebug( "Is element '" + elementDeclarations[i].getName()
+ "' a feature type definition?" );
if ( elementDeclarations[i].isSubstitutionFor( ABSTRACT_FEATURE ) ) {
LOG.logDebug( "Yes." );
FeatureType featureType = buildFeatureType( elementDeclarations[i] );
featureTypeMap.put( featureType.getName(), featureType );
} else {
LOG.logDebug( "No." );
}
}
}
/**
* Initializes the internal feature type substitution map which is used to lookup substitutions
* for feature types.
* <p>
* NOTE: As this method relies on the feature type map,
* #initializeFeatureTypeMap(ElementDeclaration[]) must have been executed before.
*
* @see #buildFeatureTypeMap(ElementDeclaration[])
*
* @param elementDeclarations
* element declarations of the feature types to process
*/
protected void buildSubstitutionMap( ElementDeclaration[] elementDeclarations ) {
Iterator iter = featureTypeMap.values().iterator();
while ( iter.hasNext() ) {
FeatureType featureType = (FeatureType) iter.next();
List<FeatureType> substitutionList = new ArrayList<FeatureType>();
LOG.logDebug( "Collecting possible substitutions for feature type '"
+ featureType.getName() + "'." );
for ( int i = 0; i < elementDeclarations.length; i++ ) {
if ( elementDeclarations[i].isAbstract() ) {
LOG.logDebug( "Skipping '" + elementDeclarations[i].getName()
+ "' as it is abstract." );
} else if ( elementDeclarations[i].isSubstitutionFor( featureType.getName() ) ) {
LOG.logDebug( "Feature type '" + elementDeclarations[i].getName()
+ "' is a concrete substitution for feature type '"
+ featureType.getName() + "'." );
FeatureType substitution = this.featureTypeMap.get( elementDeclarations[i].getName() );
substitutionList.add( substitution );
}
}
this.substitutionMap.put( featureType, substitutionList );
}
}
protected FeatureType buildFeatureType( ElementDeclaration element )
throws XMLParsingException, UnknownCRSException {
LOG.logDebug( "Building feature type from element declaration '" + element.getName()
+ "'..." );
QualifiedName name = new QualifiedName( element.getName().getLocalName(),
getTargetNamespace() );
ComplexTypeDeclaration complexType = (ComplexTypeDeclaration) element.getType().getTypeDeclaration();
ElementDeclaration[] subElements = complexType.getElements();
PropertyType[] properties = new PropertyType[subElements.length];
for ( int i = 0; i < properties.length; i++ ) {
properties[i] = buildPropertyType( subElements[i] );
}
return FeatureFactory.createFeatureType( name, element.isAbstract(), properties );
}
protected PropertyType buildPropertyType( ElementDeclaration element )
throws XMLSchemaException {
AbstractPropertyType propertyType = null;
QualifiedName propertyName = new QualifiedName( element.getName().getLocalName(),
getTargetNamespace() );
QualifiedName typeName = element.getType().getName();
int type = determinePropertyType( element );
if ( typeName == null ) {
throw new XMLSchemaException( "No type defined for the property '" + propertyName
+ "'. No inline definitions supported." );
}
if ( typeName.isInNamespace( XSDNS ) ) {
propertyType = FeatureFactory.createSimplePropertyType( propertyName, type,
element.getMinOccurs(),
element.getMaxOccurs() );
} else {
switch ( type ) {
case Types.FEATURE: {
propertyType = FeatureFactory.createFeaturePropertyType( propertyName,
element.getMinOccurs(),
element.getMaxOccurs() );
break;
}
case Types.GEOMETRY: {
propertyType = FeatureFactory.createGeometryPropertyType( propertyName, typeName,
element.getMinOccurs(),
element.getMaxOccurs() );
break;
}
default: {
// hack to make extended simple types work...
propertyType = FeatureFactory.createSimplePropertyType( propertyName, type,
element.getMinOccurs(),
element.getMaxOccurs() );
// throw new XMLSchemaException( "Unexpected type '"
// + type + "' in buildPropertyType()." );
}
}
}
return propertyType;
}
/**
* Heuristic method that tries to determine the type of GML property that is defined in an XSD
* element declaration.
*
* @param element
* <code>ElementDeclaration</code> that is a GML property definition
* @return type code from <code>Types</code>
* @throws UndefinedXSDTypeException
*
* @see Types
*/
protected final int determinePropertyType( ElementDeclaration element )
throws UndefinedXSDTypeException {
QualifiedName typeName = element.getType().getName();
LOG.logDebug( "Determining property type code for property type='" + typeName + "'..." );
int type = Types.FEATURE;
if ( element.getType().isAnonymous() ) {
LOG.logDebug( "Inline declaration. Assuming generic GML feature of some kind." );
} else if ( typeName.isInNamespace( XSDNS ) ) {
LOG.logDebug( "Must be a basic XSD type." );
try {
type = Types.getJavaTypeForXSDType( typeName.getLocalName() );
} catch ( UnknownTypeException e ) {
throw new UndefinedXSDTypeException( e.getMessage(), e );
}
} else if ( typeName.isInNamespace( GMLNS ) ) {
LOG.logDebug( "Maybe a geometry property type?" );
try {
type = Types.getJavaTypeForGMLType( typeName.getLocalName() );
LOG.logDebug( "Yes." );
} catch ( UnknownTypeException e ) {
LOG.logDebug( "No. Must be a generic GML feature of some kind." );
}
} else {
LOG.logDebug( "Should be a primitive type in our own namespace." );
if ( !typeName.isInNamespace( getTargetNamespace() ) ) {
throw new UndefinedXSDTypeException(
"Type '"
+ typeName
+ "' cannot be resolved (not in a supported namespace)." );
}
SimpleTypeDeclaration simpleType = getSimpleTypeDeclaration( typeName );
if ( simpleType == null ) {
throw new UndefinedXSDTypeException( "Simple type '" + typeName
+ "' cannot be resolved." );
}
typeName = simpleType.getRestrictionBaseType().getName();
LOG.logDebug( "Simple base type: '" + typeName + "'. Must be a basic XSD Type." );
try {
type = Types.getJavaTypeForXSDType( typeName.getLocalName() );
} catch ( UnknownTypeException e ) {
throw new UndefinedXSDTypeException( e );
}
}
return type;
}
/**
* Returns a string representation of the object.
*
* @return a string representation of the object
*/
public String toString() {
Map<FeatureType, List<FeatureType>> substitutesMap = buildSubstitutesMap();
StringBuffer sb = new StringBuffer( "GML schema targetNamespace='" );
sb.append( getTargetNamespace() );
sb.append( "'\n" );
sb.append( "\n*** " );
sb.append( featureTypeMap.size() );
sb.append( " feature type declarations ***\n" );
Iterator featureTypeIter = featureTypeMap.values().iterator();
while ( featureTypeIter.hasNext() ) {
FeatureType featureType = (FeatureType) featureTypeIter.next();
sb.append( featureTypeToString( featureType, substitutesMap ) );
if ( featureTypeIter.hasNext() ) {
sb.append( "\n\n" );
}
}
return sb.toString();
}
private Map<FeatureType, List<FeatureType>> buildSubstitutesMap() {
Map<FeatureType, List<FeatureType>> substitutesMap = new HashMap<FeatureType, List<FeatureType>>();
for ( FeatureType ft : getFeatureTypes() ) {
List<FeatureType> substitutesList = new ArrayList<FeatureType>();
for ( FeatureType substitution : getFeatureTypes() ) {
if ( isValidSubstitution( substitution, ft ) ) {
substitutesList.add( substitution );
}
}
substitutesMap.put( ft, substitutesList );
}
return substitutesMap;
}
private String featureTypeToString( FeatureType ft,
Map<FeatureType, List<FeatureType>> substitutesMap ) {
StringBuffer sb = new StringBuffer( "- Feature type '" );
sb.append( ft.getName() );
sb.append( "'\n" );
FeatureType[] substFTs = getSubstitutions( ft );
if ( substFTs.length > 0 ) {
sb.append( " is implemented by: " );
for ( int i = 0; i < substFTs.length; i++ ) {
sb.append ("'");
sb.append( substFTs[i].getName().getLocalName() );
sb.append ("'");
if ( i != substFTs.length - 1 ) {
sb.append( "," );
} else {
sb.append( "\n" );
}
}
} else {
sb.append( " has no concrete implementations?!\n" );
}
List<FeatureType> substitutesList = substitutesMap.get( ft );
sb.append( " substitutes : " );
for ( int i = 0; i < substitutesList.size(); i++ ) {
sb.append ("'");
sb.append( substitutesList.get( i ).getName().getLocalName() );
sb.append ("'");
if ( i != substitutesList.size() - 1 ) {
sb.append( "," );
}
}
sb.append( "\n" );
PropertyType[] properties = ft.getProperties();
for ( int i = 0; i < properties.length; i++ ) {
sb.append( " + '" );
sb.append( properties[i].getName() );
sb.append( "', type: " );
try {
sb.append( Types.getTypeNameForSQLTypeCode( properties[i].getType() ) );
} catch ( UnknownTypeException e ) {
sb.append( "unknown" );
}
sb.append( ", min: " );
sb.append( properties[i].getMinOccurs() );
sb.append( ", max: " );
sb.append( properties[i].getMaxOccurs() );
if ( i != properties.length - 1 ) {
sb.append( "\n" );
}
}
return sb.toString();
}
}
/* ********************************************************************
Changes to this class. What the people have been up to:
$Log$
Revision 1.26 2006/11/27 09:07:53 poth
JNI integration of proj4 has been removed. The CRS functionality now will be done by native deegree code.
Revision 1.25 2006/11/23 15:23:35 mschneider
Improved #toString().
Revision 1.24 2006/08/29 19:54:14 poth
footer corrected
Revision 1.23 2006/08/22 18:14:42 mschneider
Refactored due to cleanup of org.deegree.io.datastore.schema package.
Revision 1.22 2006/08/21 15:48:46 mschneider
Changes due to removing of (unused + outdated) FeatureArrayPropertyType.
Revision 1.21 2006/07/12 14:46:14 poth
comment footer added
********************************************************************** */