//$Header: /home/deegree/jail/deegreerepository/deegree/src/org/deegree/model/feature/DefaultFeature.java,v 1.46 2006/10/16 09:34:59 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.model.feature; import java.io.Serializable; 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.io.datastore.PropertyPathResolvingException; import org.deegree.model.feature.schema.FeatureType; import org.deegree.model.feature.schema.PropertyType; import org.deegree.model.spatialschema.Geometry; import org.deegree.model.spatialschema.GeometryFactory; import org.deegree.ogcbase.CommonNamespaces; import org.deegree.ogcbase.PropertyPath; import org.deegree.ogcbase.PropertyPathStep; /** * Features are, according to the Abstract Specification, digital representations of real world * entities. Feature Identity thus refers to mechanisms to identify such representations: not to * identify the real world entities that are the subject of a representation. Thus two different * representations of a real world entity (say the Mississippi River) will be two different features * with distinct identities. Real world identification systems, such as title numbers, while * possibly forming a sound basis for an implementation of a feature identity mechanism, are not of * themselves such a mechanism. * * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider</a> * * @author last edited by: $Author: poth $ * * @version $Revision: 1.46 $ $Date: 2006/10/16 09:34:59 $ */ public class DefaultFeature extends AbstractFeature implements Serializable { private static final long serialVersionUID = -1636744791597960465L; // key class: QualifiedName // value class: FeatureProperty [] protected Map propertyMap; protected FeatureProperty[] properties; protected Object[] propertyValues; protected Geometry[] geometryPropertyValues; /** * Creates a new instance of <code>DefaultFeature</code> from the given parameters. * * @param id * @param featureType * @param properties * properties of the new feature, given in their intended order */ protected DefaultFeature( String id, FeatureType featureType, FeatureProperty[] properties ) { this( id, featureType, properties, null ); } /** * Creates a new instance of <code>DefaultFeature</code> from the given parameters. * * @param id * @param featureType * @param properties * properties of the new feature, given in their intended order * @param owner */ protected DefaultFeature( String id, FeatureType featureType, FeatureProperty[] properties, FeatureProperty owner ) { super( id, featureType, owner ); for ( int i = 0; i < properties.length; i++ ) { FeatureProperty property = properties[i]; URI namespace = property.getName().getNamespace(); if ( ( namespace == null ) ) { PropertyType propertyType = featureType.getProperty( property.getName() ); if ( propertyType == null ) { throw new IllegalArgumentException( "Unknown property '" + property.getName() + "' for feature with type '" + featureType.getName() + "': the feature type has no such property." ); } } else if ( ( !namespace.equals( CommonNamespaces.GMLNS ) ) ) { PropertyType propertyType = featureType.getProperty( property.getName() ); if ( propertyType == null ) { throw new IllegalArgumentException( "Unknown property '" + property.getName() + "' for feature with type '" + featureType.getName() + "': the feature type has no such property." ); } } } this.properties = properties; } /** * TODO type checks! * * @throws FeatureException */ public void validate() throws FeatureException { if ( this.propertyMap == null ) { this.propertyMap = buildPropertyMap(); } PropertyType[] propertyTypes = featureType.getProperties(); for ( int i = 0; i < propertyTypes.length; i++ ) { List propertyList = (List) this.propertyMap.get( propertyTypes[i].getName() ); if ( propertyList == null ) { if ( propertyTypes[i].getMinOccurs() != 0 ) { throw new FeatureException( "Feature is not a valid instance of type '" + featureType.getName() + "', mandatory property '" + propertyTypes[i].getName() + "' is missing." ); } } else { if ( propertyTypes[i].getMinOccurs() > propertyList.size() ) { throw new FeatureException( "Feature is not a valid instance of type '" + featureType.getName() + "', property '" + propertyTypes[i].getName() + "' has minOccurs=" + propertyTypes[i].getMinOccurs() + ", but is only present " + propertyList.size() + " times." ); } if ( propertyTypes[i].getMaxOccurs() != -1 && propertyTypes[i].getMaxOccurs() < propertyList.size() ) { throw new FeatureException( "Feature is not a valid instance of type '" + featureType.getName() + "', property '" + propertyTypes[i].getName() + "' has maxOccurs=" + propertyTypes[i].getMaxOccurs() + ", but is present " + propertyList.size() + " times." ); } } } } /** * Builds a map for efficient lookup of the feature's properties by name. * * @return key class: QualifiedName, value class: FeatureProperty [] */ private Map buildPropertyMap() { Map propertyMap = new HashMap(); for ( int i = 0; i < this.properties.length; i++ ) { List propertyList = (List) propertyMap.get( this.properties[i].getName() ); if ( propertyList == null ) { propertyList = new ArrayList(); } propertyList.add( properties[i] ); propertyMap.put( properties[i].getName(), propertyList ); } Iterator propertyNameIter = propertyMap.keySet().iterator(); while ( propertyNameIter.hasNext() ) { QualifiedName propertyName = (QualifiedName) propertyNameIter.next(); List propertyList = (List) propertyMap.get( propertyName ); propertyMap.put( propertyName, propertyList.toArray( new FeatureProperty[propertyList.size()] ) ); } return propertyMap; } /** * Builds a map for efficient lookup of the feature's properties by name. * * @return key class: QualifiedName, value class: FeatureProperty [] */ private Geometry[] extractGeometryPropertyValues() { List geometryPropertiesList = new ArrayList(); for ( int i = 0; i < this.properties.length; i++ ) { if ( this.properties[i].getValue() instanceof Geometry ) { geometryPropertiesList.add( this.properties[i].getValue() ); } else if ( this.properties[i].getValue() instanceof Object[] ) { Object[] objects = (Object[]) this.properties[i].getValue(); for ( int j = 0; j < objects.length; j++ ) { if ( objects[j] instanceof Geometry ) { geometryPropertiesList.add( objects[j] ); } } } } Geometry[] geometryPropertyValues = new Geometry[geometryPropertiesList.size()]; geometryPropertyValues = (Geometry[]) geometryPropertiesList.toArray( geometryPropertyValues ); return geometryPropertyValues; } /** * Returns all properties of the feature in their original order. * * @return all properties of the feature */ public FeatureProperty[] getProperties() { return this.properties; } /** * Returns the properties of the feature with the given name in their original order. * * @return the properties of the feature with the given name or null if the feature has no * property with that name */ public FeatureProperty[] getProperties( QualifiedName name ) { if ( this.propertyMap == null ) { this.propertyMap = buildPropertyMap(); } FeatureProperty[] properties = (FeatureProperty[]) propertyMap.get( name ); return properties; } /** * Returns the property of the feature identified by the given PropertyPath in their original * order. * * NOTE: Current implementation does not handle multiple properties (on the path) or index * addressing in the path. * * @return the properties of the feature identified by the given PropertyPath or null if the * feature has no such properties * * @see PropertyPath */ public FeatureProperty getDefaultProperty( PropertyPath path ) throws PropertyPathResolvingException { Feature currentFeature = this; FeatureProperty currentProperty = null; for ( int i = 0; i < path.getSteps(); i += 2 ) { QualifiedName propertyName = path.getStep( i ).getPropertyName(); currentProperty = currentFeature.getDefaultProperty( propertyName ); if ( i + 1 < path.getSteps() ) { QualifiedName featureName = path.getStep( i + 1 ).getPropertyName(); Object value = currentProperty.getValue(); if ( !( value instanceof Feature ) ) { throw new PropertyPathResolvingException( "PropertyPath '" + path + "' cannot be matched to feature. Value of property '" + propertyName + "' is not a feature, but the path does not stop there." ); } currentFeature = (Feature) value; if ( !featureName.equals( currentFeature.getName() ) ) { throw new PropertyPathResolvingException("PropertyPath '" + path + "' cannot be matched to feature. Property '" + propertyName + "' contains a feature with name '" + currentFeature.getName() + "', but requested was: '" + featureName + "'." ); } } } if ( currentProperty == null ) { List<PropertyPathStep> list = path.getAllSteps(); currentProperty = new DefaultFeatureProperty( list.get( list.size()-1 ).getPropertyName(), null ); } return currentProperty; } /** * Returns the first property of the feature with the given name. * * @return the first property of the feature with the given name or null if the feature has no * such property */ public FeatureProperty getDefaultProperty( QualifiedName name ) { FeatureProperty[] properties = getProperties( name ); if ( properties != null ) { return properties[0]; } return new DefaultFeatureProperty(name, null ); } /** * Returns the properties of the feature at the submitted index of the feature type definition. * * @return the properties of the feature at the submitted index * @deprecated */ public FeatureProperty[] getProperties( int index ) { QualifiedName s = featureType.getPropertyName( index ); return getProperties( s ); } /** * Returns the values of all geometry properties of the feature. * * @return the values of all geometry properties of the feature, or a zero-length array if the * feature has no geometry properties */ public Geometry[] getGeometryPropertyValues() { if ( this.geometryPropertyValues == null ) { this.geometryPropertyValues = extractGeometryPropertyValues(); } return this.geometryPropertyValues; } /** * Returns the value of the default geometry property of the feature. If the feature has no * geometry property, this is a Point at the coordinates (0,0). * * @return default geometry or Point at (0,0) if feature has no geometry */ public Geometry getDefaultGeometryPropertyValue() { Geometry[] geometryValues = getGeometryPropertyValues(); if ( geometryValues.length < 1 ) { return GeometryFactory.createPoint( 0, 0, null ); } return geometryValues[0]; } /** * Sets the value for the given property. The index is needed to specify the occurences of the * property that is to be replaced. Set to 0 for properties that may only occur once. * * @param property * property name and the property's new value * @param index * position of the property that is to be replaced */ public void setProperty( FeatureProperty property, int index ) { FeatureProperty[] oldProperties = getProperties( property.getName() ); if ( oldProperties == null ) { throw new IllegalArgumentException( "Cannot set property '" + property.getName() + "': feature has no property with that name." ); } if ( index > oldProperties.length - 1 ) { throw new IllegalArgumentException( "Cannot set property '" + property.getName() + "' with index " + index + ": feature has only " + oldProperties.length + " properties with that name." ); } oldProperties[index].setValue( property.getValue() ); this.geometryPropertyValues = extractGeometryPropertyValues(); } /** * Adds the given property to the feature's properties. The position of the property is * determined by the feature type. If the feature already has properties with this name, the * new one is inserted directly behind them. * * @param property * property to insert */ public void addProperty( FeatureProperty property ) { FeatureProperty[] newProperties; if ( this.properties == null ) { newProperties = new FeatureProperty[] { property }; } else { newProperties = new FeatureProperty[this.properties.length + 1]; for ( int i = 0; i < this.properties.length; i++ ) { newProperties[i] = this.properties[i]; } // TODO insert at correct position newProperties[this.properties.length] = property; } this.properties = newProperties; this.propertyMap = buildPropertyMap(); this.geometryPropertyValues = extractGeometryPropertyValues(); } /** * Removes the properties with the given name. * * @param propertyName * name of the properties to remove */ public void removeProperty( QualifiedName propertyName ) { // TODO } /** * Replaces the given property with a new one. * * @param oldProperty * property to be replaced * @param newProperty * new property */ public void replaceProperty( FeatureProperty oldProperty, FeatureProperty newProperty ) { for ( int i = 0; i < properties.length; i++ ) { if ( properties[i] == oldProperty ) { properties[i] = newProperty; } } } /** * Returns a string representation of the object. * * @return a string representation of the object */ public String toString() { String ret = getClass().getName(); /* ret = "\nid = " + getId() + "\n"; ret += "featureType = " + featureType + "\n"; ret += "geoProps = " + geometryPropertyValues + "\n"; ret += "properties = " + propertyMap + "\n"; */ return ret; } }/* ******************************************************************** Changes to this class. What the people have been up to: $Log: DefaultFeature.java,v $ Revision 1.46 2006/10/16 09:34:59 poth enbaled default value return for Feature.getDefaultProperty and FeatureProperty.getValue Revision 1.45 2006/08/30 16:59:43 mschneider Improved javadoc. Revision 1.44 2006/08/24 06:40:27 poth File header corrected Revision 1.43 2006/07/12 14:46:19 poth comment footer added ********************************************************************** */