/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
*
* 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;
* version 2.1 of the License.
*
* 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.
*/
package org.geotools.feature;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import org.opengis.feature.simple.SimpleFeature;
import org.geotools.geometry.jts.ReferencedEnvelope;
/**
* <p>
* Represents a feature of arbitrary complexity.
* <p>
* This interface answers the question: How do we store feature attributes?
* (The answer to the more useful question, How do we access feature attribute,
* is contained in the Expression class.
* <p>
* <p>
* Warning: We are revising the Feature Model to be more complete in the
* next round of GeoTools. If you do any data access in your classes please try
* and make use of Expression to access your information, if you do this you will
* not be affected by the change (beyond a few search and replace operations).
* </p>
* The most generic approach would be to pass all feature attributes as objects
* and use Java variable and method references to access them. However, this is
* also the most useless approach because it establishes no unified methods for getting
* attribute information (since it is totally Object dependent), without
* elaborate reflection/introspection, which is inconvenient to use. Unlike
* its {@link FeatureType} counterpart, this interface does not attempt to
* serve as a typing framework. Rather, multiple implementations of this
* interface should generally be for performance reasons.
* </p>
*
* <p>
* This interface serves two important purposes. Most obviously, it gives
* users of features a unified, consistent framework for accessing and
* manipulating feature data. Perhaps more importantly, the
* <code>FeatureType</code> and <code>Feature</code> interfaces also work
* together to give implementers a framework for constraining and enforcing
* constraints (respectively) on allowed feature types. As such, this
* interface is as general as possible in terms of the types of objects to
* which it provides access. Keep in mind that creating new features is
* relatively difficult and should only be done to optimize performance for
* highly constrained schema types. For the vast majority of schemas, the
* generic feature implementation will work fine.
* </p>
*
* <p>
* <b>Notes for Feature Clients:</b><br>
* Clients should always use feature accessor methods (getAttribute and
* setAttribute) to modify the state of internal attribute objects. It is
* possible that some feature implementations will allow object state changes
* by clients outside of the class, but this is strongly discouraged. In
* general, feature implementations will make defensive copies of objects
* passed to clients and it is therefore not guaranteed that client state
* changes that take place outside of the feature will be reflected in the
* internal state of the feature object! <i>For this reason, clients should
* always use the set methods to change feature attribute object states!</i>
* </p>
*
* <p>
* <b>Notes for Feature Implementers:</b><br>
* It is the responsibility of the implementing class to ensure that the
* <code>Feature</code> attributes stay synchronized with its FeatureType
* definition. <i>Features should never get out of synch with their declared
* schemas and should never alter their schemas!</i> There are four
* conventions of which implementers of this interface must be aware in order
* to successfully manage a <code>Feature</code>:
* </p>
*
* <ol>
* <li>
* <b>FeatureType Reference</b><br>
* Features must always hold a single (immutable: see
* <code>FeatureType</code>) schema reference and this reference should not be
* altered after a feature has been created. To ensure this, is is strongly
* recommended that features take a valid reference to an existing immutable
* schema in its constructor and declare that reference final.
* </li>
* <li>
* <b>Default Geometry</b><br>
* Each feature must have a default geometry, but this primary geometry may be
* null. This means that a feature may contain no geometries, but it must
* always have a method for accessing a geometry object (even if it is null).
* It also means that a feature with multiple geometries must pick one as its
* default geometry. Note that the designation of the default geometry is
* stored as part of the <code>FeatureType</code> and is therefore immmutable.
* </li>
* <li>
* <b>Attributes</b><br> All features contain zero or more attributes, which
* can have one or more occurrences inside the feature. Attributes may be any
* valid Java object. If attributes are instances of <code>Feature</code>,
* they are handled specially by the <code>Feature</code> methods, in that
* their attributes may be accessed directly by their containing feature. All
* other object variables and methods must be accessed through the objects
* themselves. It is up to implementers of <code>Feature</code> to make sure
* that each attribute value conforms to its internal schema. A feature
* should never reach a state where its attributes (or sub-attributes) do not
* conform to their <code>FeatureType</code> definitions. There are three
* ways to implement this. The first is to simply make features immutable;
* however, given the ubiquity and required flexibility of features, this is
* likely not possible. The second (and second easiest), is to make all
* feature attributes immutable. For most cases, this is probably the best way
* to handle this issue. The third way, is to never give out a reference that
* would allow a client to change an attribute object's class (most obviously,
* an array reference). Generally speaking, features should attempt to
* minimize external object references by attempting to clone incoming
* attributes before adding them and outgoing attributes before sending them.
* For features with non-cloneable attributes, of course, this is not
* possible, so this is left to the discretion of the implementor.
* </li>
* <li>
* <b>Constructors</b><br> Constructors should take arguments with enough
* information to create a valid representation of the feature. They should
* also always include a valid schema that can be used to check the proposed
* attributes. This is necessary to ensure that the feature is always in a
* valid state, relative to its schema.
* </li>
* <li>
* <b>hashCode() and equals(Object other)</b><br>
* Determining equality and equivalence for Feature instances is of utmost
* importance. This must be done in a constistent manner, as many other areas
* of geotools will rely on these relations. See java.lang.Object for details.
* </li>
* </ol>
*
*
* @author James Macgill, CCG
* @author Rob Hranac, TOPP
* @author Ian Schneider, USDA-ARS
* @author dzwiers
* @source $URL$
* @version $Id$
*
* @see org.geotools.feature.FeatureType
* @see org.geotools.feature.DefaultFeature
*
* @deprecated use {@link SimpleFeature}.
*/
public interface Feature extends SimpleFeature {
/**
* Gets a reference to the schema for this feature.
*
* @return A reference to this feature's schema.
*/
FeatureType getFeatureType();
/**
* Gets the unique feature ID for this feature.
*
* @return Unique identifier for this feature.
*/
String getID();
/**
* Copy all the attributes of this Feature into the given array. If the
* argument array is null, a new one will be created. Gets all attributes
* from this feature, returned as a complex object array. This array
* comes with no metadata, so to interpret this collection the caller
* class should ask for the schema as well. This array may have multiple
* entries for each AttributeType depending on the AttributeType's
* multiplicity.
*
* @param attributes An array to copy attributes into. May be null.
*
* @return The array passed in, or a new one if null.
*/
Object[] getAttributes(Object[] attributes);
/**
* Gets an attribute for this feature at the location specified by xPath.
* Due to the complex nature of xpath, a List of all valid values will be
* returned when an attribute is requested. These values can range from
* complete Feature Collections to single primative attribute values. When
* a particular instance of the Attribute is requested, then the Object
* will be returned. Simply put, in the general case we are assuming the
* existance of multiplicity, so specify which instance you want or you
* get them all. Example of getting a list: ./road returns the
* List of road attribute instances Example of getting an Object:
* ./road[0] returns the first road
*
* @param xPath XPath representation of attribute location.
*
* @return A List of copies of the requested attribute, null if the
* requested xpath is not found, or NULL_ATTRIBUTE.
*/
Object getAttribute(String xPath);
/**
* Gets an attribute by the given zero-based index. This index is based on
* the values within the Feature as opposed to the AttributeType
* declaration. To get the values for the 5th attributeType, use the
* schema to determine the xPath and class the getAttribute(xPath)
* method.
*
* @param index The requested index. Must be 0 <= idx <
* getNumberOfAttributes().
*
* @return A copy of the requested attribute, or NULL_ATTRIBUTE.
*
* @see Feature#getAttribute(String)
*/
Object getAttribute(int index);
/**
* Sets an attribute by the given zero-based index. This index is based on
* the values within the Feature as opposed to the AttributeType
* declaration. To get the values for the 5th attributeType, use the
* schema to determine the xPath and class the setAttribute(xPath,val)
* method.
*
* @param position The requested index. Must be 0 <= idx <
* getNumberOfAttributes()
* @param val An object representing the attribute being set
*
* @throws IllegalAttributeException if the passed in val does not validate
* against the AttributeType at that position.
* @throws ArrayIndexOutOfBoundsException if an invalid position is given
*
* @see Feature#setAttribute(String, Object)
*/
void setAttribute(int position, Object val);
/**
* Get the number of attributes this feature has. This is NOT simply a
* convenience method for calling
* getFeatureType().getNumberOfAttributes(). This is the same as calling
* getAttributes(null).length. This represents the number of actual
* attribute values in the feature, and may differ from the number of
* AttributeTypes defined in the FeatureType based on the multiplicity of
* the AttributeTypes.
*
* @return The total number of attributes this Feature contains.
*/
int getNumberOfAttributes();
/**
* Sets a single attribute for this feature, passed as a complex object. If
* the attribute does not exist or the object does not conform to the
* internal schema, an exception is thrown. Checking this is, of course,
* left to the feature to do internally. Well behaved features should
* always fully check the passed attributes against thire schema before
* adding them. NOTE: The xPath may contain instance information about
* multiplicity, for example: ./road[3] which is the third road
* attribute in this feature.
*
* @param xPath XPath representation of attribute location.
* @param attribute Feature attribute to set.
*
* @throws IllegalAttributeException If the attribute is illegal for the
* path specified.
*/
void setAttribute(String xPath, Object attribute) throws IllegalAttributeException;
/**
* Gets the default geometry for this feature.
*
* <p>
* This method will return <code>null</code> if no DefaultGeometry has been
* defined by the schema.
* </p>
*
* @return Default geometry for this feature, or <code>null</code>
*/
Geometry getDefaultGeometry();
/**
* Sets the default geometry for this feature.
*
* @param geometry The geometry to set.
*
* @throws IllegalAttributeException If the AttributeType is not a
* geometry, or is invalid for some other reason.
*/
void setDefaultGeometry(Geometry geometry) throws IllegalAttributeException;
/**
* Get the total bounds of this feature which is calculated by doing a
* union of the bounds of each geometry this feature is associated with.
*
* <p>
* This method will return an empty Envelope if the feature contains no
* geometry information.
* </p>
*
* @return An Envelope containing the total bounds of this Feature.
*/
ReferencedEnvelope getBounds();
/**
* Not straight forward, this is a "null" object to represent the value
* null for a given attribute which is nullable.
*/
static final class NULL implements Comparable {
/**
* Implementation of Comparable.
*
* @param o The other thing to compare to.
*
* @return 0 if null or this, 1 for all others.
*/
public int compareTo(Object o) {
if (o == null) {
return 0;
}
if (o == this) {
return 0;
}
return 1;
}
}
}