//$Header: /home/deegree/jail/deegreerepository/deegree/src/org/deegree/io/datastore/PropertyPathResolver.java,v 1.27 2006/09/26 16:42:12 mschneider 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.io.datastore;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.deegree.datatypes.QualifiedName;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.log.LoggerFactory;
import org.deegree.i18n.Messages;
import org.deegree.io.datastore.schema.MappedFeatureType;
import org.deegree.io.datastore.schema.MappedPropertyType;
import org.deegree.model.feature.schema.PropertyType;
import org.deegree.ogcbase.ElementStep;
import org.deegree.ogcbase.PropertyPath;
import org.deegree.ogcbase.PropertyPathFactory;
import org.deegree.ogcbase.PropertyPathStep;
import org.deegree.ogcwebservices.wfs.operation.GetFeature;
/**
* Helper class that resolves {@link PropertyPath} instances (e.g. PropertyName
* elements in {@link GetFeature}) against the property structure of {@link MappedFeatureType}s.
*
* @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
* @author last edited by: $Author: mschneider $
*
* @version $Revision: 1.27 $, $Date: 2006/09/26 16:42:12 $
*/
public class PropertyPathResolver {
private static final ILogger LOG = LoggerFactory.getLogger( PropertyPathResolver.class );
/**
* Ensures that the requested property begins with a "feature type step".
* <p>
* This is necessary, as section 7.4.2 of the Web Feature Implementation Specification 1.1.0 states:
* </p>
* <p>
* The first step of a relative location path <b>may</b> correspond to the root element of the
* feature property being referenced <b>or</b> to the root element of the feature type with the
* next step corresponding to the root element of the feature property being referenced.
* </p>
*
* @param ft featureType that the requested properties refer to
* @param path requested property
* @return normalized path, i.e. it begins with a feature type step
*/
public static PropertyPath normalizePropertyPath( MappedFeatureType ft, PropertyPath path ) {
QualifiedName featureTypeName = ft.getName();
if ( !featureTypeName.equals( path.getStep( 0 ).getPropertyName() ) ) {
path.prepend( PropertyPathFactory.createPropertyPathStep( featureTypeName ) );
}
return path;
}
/**
* Ensures that all requested properties begin with a feature type step.
* <p>
* If no properties are specified at all, a single PropertyPath entry is created that selects
* the whole feature type.
* </p>
*
* @param ft feature type that the requested properties refer to
* @param paths requested properties, may not be null
* @return normalized paths
*/
public static PropertyPath[] normalizePropertyPaths( MappedFeatureType ft, PropertyPath[] paths ) {
for ( int i = 0; i < paths.length; i++ ) {
paths[i] = normalizePropertyPath( ft, paths[i] );
}
if ( paths.length == 0 ) {
QualifiedName featureTypeName = ft.getName();
paths = new PropertyPath[] { PropertyPathFactory.createPropertyPath( featureTypeName ) };
}
return paths;
}
/**
* Determines the properties of the given feature type that have to be fetched based on the requested
* property paths.
* <p>
* Returns a helper <code>Map</code> that associates each (requested) property of the feature
* with the property paths that request it. Note that the returned helper map may contain more
* properties than specified. This behaviour is due to section 9.2 of the Web Feature
* Implementation Specification 1.1.0:
* </p>
* <p>
* In the event that a WFS encounters a query that does not select all mandatory properties of
* a feature, the WFS will internally augment the property name list to include all mandatory
* property names.
* </p>
* <p>
* Note that every requested property path must begin with a step that selects the given feature
* type.
* </p>
*
* @param ft feature type
* @param requestedPaths
* @return <code>Map</code>, key class: <code>MappedPropertyType</code>, value class:
* <code>Collection</code> (of <code>PropertyPath</code> instances)
* @throws PropertyPathResolvingException
*/
public static Map<MappedPropertyType, Collection<PropertyPath>> determineFetchProperties(
MappedFeatureType ft,
PropertyPath[] requestedPaths )
throws PropertyPathResolvingException {
Map<MappedPropertyType, Collection<PropertyPath>> requestedMap = null;
requestedMap = determineRequestedProperties( ft, requestedPaths );
requestedMap = augmentFetchProperties( ft, requestedMap );
return requestedMap;
}
/**
* Builds a map that associates each requested property of the feature with the property paths
* that request it. The property path may well select a property of a subfeature, but the key
* values in the map are always (direct) properties of the given feature type.
*
* @param ft feature type
* @param requestedPaths
* @return map that associates each requested property with the property paths that request it
* @throws PropertyPathResolvingException
*/
private static Map<MappedPropertyType, Collection<PropertyPath>> determineRequestedProperties(
MappedFeatureType ft,
PropertyPath[] requestedPaths )
throws PropertyPathResolvingException {
Map<MappedPropertyType, Collection<PropertyPath>> propertyMap = new LinkedHashMap<MappedPropertyType, Collection<PropertyPath>>();
for ( int i = 0; i < requestedPaths.length; i++ ) {
PropertyPath requestedPath = requestedPaths[i];
if ( requestedPath.getStep( 0 ).getPropertyName().equals( ft.getName() ) ) {
if ( requestedPath.getSteps() == 1 ) {
// path requests the whole feature
PropertyType[] allProperties = ft.getProperties();
for ( int j = 0; j < allProperties.length; j++ ) {
Collection<PropertyPath> paths = propertyMap.get( allProperties[j] );
if ( paths == null ) {
paths = new ArrayList<PropertyPath>();
}
PropertyPath newPropertyPath = PropertyPathFactory.createPropertyPath( ft.getName() );
newPropertyPath.append( PropertyPathFactory.createPropertyPathStep( allProperties[j].getName() ) );
paths.add( newPropertyPath );
propertyMap.put( (MappedPropertyType) allProperties[j], paths );
}
} else {
// path requests a certain property
QualifiedName propertyName = requestedPath.getStep( 1 ).getPropertyName();
PropertyType property = ft.getProperty( propertyName );
if ( property == null ) {
String msg = Messages.getMessage( "DATASTORE_NO_SUCH_PROPERTY2",
requestedPath, ft.getName(), propertyName );
throw new PropertyPathResolvingException( msg );
}
Collection<PropertyPath> paths = propertyMap.get( property );
if ( paths == null ) {
paths = new ArrayList<PropertyPath>();
}
paths.add( requestedPath );
propertyMap.put( (MappedPropertyType) property, paths );
}
} else {
String msg = "Internal error in PropertyPathResolver: no property with name '"
+ requestedPath + "' in feature type '" + ft.getName() + "'.";
throw new PropertyPathResolvingException( msg );
}
}
return propertyMap;
}
/**
* Returns an augmented version of the input map that contains additional entries for all
* mandatory properties of the feature type.
*
* @param ft feature type
* @param requestedMap
* @return augmented version of the input map
* @throws PropertyPathResolvingException
*/
private static Map<MappedPropertyType, Collection<PropertyPath>> augmentFetchProperties(
MappedFeatureType ft,
Map<MappedPropertyType, Collection<PropertyPath>> requestedMap ) {
if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
LOG.logDebug( "Properties to be fetched for feature type '" + ft.getName() + "':" );
}
Map<MappedPropertyType, Collection<PropertyPath>> augmentedMap = new LinkedHashMap<MappedPropertyType, Collection<PropertyPath>>();
PropertyType[] allProperties = ft.getProperties();
for ( int i = 0; i < allProperties.length; i++ ) {
MappedPropertyType property = (MappedPropertyType) allProperties[i];
Collection<PropertyPath> requestingPaths = requestedMap.get( property );
if ( requestingPaths != null ) {
LOG.logDebug( "- " + property.getName() );
augmentedMap.put( property, requestingPaths );
for ( PropertyPath path : requestingPaths ) {
LOG.logDebug( " - Requested by path: '" + path + "'" );
}
} else if ( property.getMinOccurs() > 0 ) {
LOG.logDebug( "- " + property.getName() + " (augmented)" );
Collection<PropertyPath> mandatoryPaths = new ArrayList<PropertyPath>();
List<PropertyPathStep> stepList = new ArrayList<PropertyPathStep>( 2 );
stepList.add( new ElementStep( ft.getName() ) );
stepList.add( new ElementStep( property.getName() ) );
PropertyPath mandatoryPath = new PropertyPath( stepList );
mandatoryPaths.add( mandatoryPath );
augmentedMap.put( property, mandatoryPaths );
}
}
return augmentedMap;
}
/**
* Determines the sub property paths that are needed to fetch the given property paths for the
* also given property.
*
* @param featureType
* @param propertyPaths
* @return sub property paths that are needed to fetch the given property paths
*/
public static PropertyPath[] determineSubPropertyPaths( MappedFeatureType featureType,
Collection<PropertyPath> propertyPaths ) {
Collection<PropertyPath> subPropertyPaths = new ArrayList<PropertyPath>();
Iterator iter = propertyPaths.iterator();
while ( iter.hasNext() ) {
PropertyPath path = (PropertyPath) iter.next();
if ( path.getSteps() > 2 ) {
subPropertyPaths.add( PropertyPathFactory.createPropertyPath( path, 2,
path.getSteps() ) );
} else {
PropertyType[] subProperties = featureType.getProperties();
for ( int i = 0; i < subProperties.length; i++ ) {
PropertyPath subPropertyPath = PropertyPathFactory.createPropertyPath( featureType.getName() );
subPropertyPath.append( PropertyPathFactory.createPropertyPathStep( subProperties[i].getName() ) );
subPropertyPaths.add( subPropertyPath );
}
}
}
if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
LOG.logDebug( "Original property paths:" );
for ( PropertyPath path : propertyPaths ) {
LOG.logDebug( "- '" + path + "'" );
}
LOG.logDebug( "Sub feature property paths:" );
for ( PropertyPath path : subPropertyPaths ) {
LOG.logDebug( "- '" + path + "'" );
}
}
PropertyPath[] subPaths = subPropertyPaths.toArray( new PropertyPath[subPropertyPaths.size()] );
return subPaths;
}
}
/* ********************************************************************
Changes to this class. What the people have been up to:
$Log: PropertyPathResolver.java,v $
Revision 1.27 2006/09/26 16:42:12 mschneider
Javadoc corrections + fixed warnings.
Revision 1.26 2006/09/20 11:35:41 mschneider
Merged datastore related messages with org.deegree.18n.
Revision 1.25 2006/08/06 20:23:48 poth
unneccessary type cast removed
Revision 1.24 2006/08/06 20:21:30 poth
never thrown exception removed from augmentFetchProperties
Revision 1.23 2006/06/01 15:23:41 mschneider
Renamed determineRequestedProperties() to determineFetchProperties(). Changed behaviour: not-requested, but mandatory properties are now augmented to be always fetched.
Revision 1.22 2006/06/01 13:09:32 mschneider
Fixed footer.
********************************************************************** */