/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2002-2006, GeoTools Project Managment Committee (PMC)
 *
 *    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 java.net.URI;


/**
 * A metadata template for a Feature of arbitrary complexity.
 * <p>
 * Notes:
 * <ul>
 * <li>that this documentation should be read in conjunction with the {@link Feature}API.
 * <li>the attributes described by this FeatureType <b>and its ancestors </b> define the complete
 * schema for a Feature
 * </p>
 * <p>
 * This interface answers the question: How do we represent features within GeoTools? Of course, the
 * most general answer would be: features can be any Java object. However, this is also the least
 * useful solution because it means that users of features have essentially no way to find out about
 * the meaning of features other than using Java introspection/reflection. This is too cumbersome
 * and is insufficient for the goal of creating a simple framework for manipulating and accessing
 * generic geographic data. The opposite approach might be to define a very constrained set of
 * possible attributes (that, for example, mirrored Java primitives and OGC simple geometries) and
 * only allow features of this type.
 * </p>
 * <p>
 * This interface takes a different approach: it defines a minimal ontology for representing a
 * feature and serves as a consistent framework for defining more constrained (and, therefore, often
 * more meaningful) feature types. A <code>FeatureType</code> represents features as an object
 * that contains zero or more attribute objects, one of which will generally be a geometry, but no
 * geometry and multiple geometries are allowed, according to implementation. Note that instances of
 * implementations of this class are henceforth referred to as schemas.
 * </p>
 * <p>
 * With oneexceptions, the type of an attribute is considered to be its cannonical definition by the
 * FeatureType. For example, an attribute type might be a <code>javax.sound.midi.Sequence</code>
 * object, which contains a <code>float</code> public field called PPQ. The fact that this
 * attribute exists is not known by the <code>FeatureType</code> itself. If a caller asks this
 * <code>FeatureType</code> for all of its attributes, the <code>
 * FeatureType</code> will tell
 * the caller that it has an attribute of type <code>javax.sound.midi.Sequence</code>, but not
 * that this attribute has a sub-attribute (field) called PPQ. It is the responsibility of the
 * callers to understand the objects it is asking for and manipulate them appropriately.
 * </p>
 * <p>
 * The exceptions is for:
 * <ul>
 * <li>if the type stored in the <code>FeatureType</code> is a
 * <code>org.geotools.datasource.Feature</code> type. <br>
 * In this case, all information about sub-attributes are stored and passed to calling classes upon
 * request. The style of reference (XPath) is defined in and mediated by <code>FeatureType</code>
 * implementations.
 * </ul>
 * Question: how does one determine the schema for the attribute defined as a FeatureType? I suspect
 * that FeatureType may be a valid AttributeType? (One needs this schema information before xpath
 * can be used to query the Feature value.
 * </p>
 * <p>
 * It is the responsibility of the implementing class to ensure that the <code>FeatureType</code>
 * is always in a valid state. This means that each attribute tuple must be fully initialized and
 * valid. The minimum valid <code>FeatureType</code> is one with nulls for namespace, type, and
 * attributes; this is clearly a trivial case, since it is so constrained that it would not allow
 * for any feature construction. There are a few conventions of which implementers of this interface
 * must be aware in order to successfully manage a <code>FeatureType</code>:
 * </p>
 * <ol>
 * <li><b>Immutability </b> <br>
 * <i>FeatureTypes must be implemented as immutable objects! </i> All setting methods have been
 * removed from this interface, that functionality is now available in the mutable {@link
 * FeatureTypeFactory}</li>
 * <li><b>Default Geometries </b> <br>
 * Note that the FeatureType contains a special methods for handling geometries. The primary
 * geometry retrieval methods are in <code>Feature</code> because they may change over the life of
 * the feature, while the schema may not. In cases where there are more than one geometries it is up
 * to the implementor to determine which is the default geometry. <code>getDefaultGeometry</code>
 * may return <code>null</code> if there are no geometries in the FeatureType, but if there is one
 * or more geometry then the method must return one of them, <code>null</code> is never an
 * acceptable return value.</li>
 * <li><b>XPath </b> <br>
 * XPath is the standard used to access all attributes (flat, nested, and multiple), via a single,
 * unified string. Using XPath to access attributes has the convenient side-benefit of making them
 * appear to be non-nested and non-multiple to callers with no awareness of XPath. This greatly
 * simplifies accessing and manipulating data. However, it does put extra burden on the implementers
 * of <code>FeatureType</code> to understand and correctly implement XPath pointers. Note that the
 * <code>Feature</code> object does not understand XPath at all and relies on implementors of this
 * interface to interpret XPath references. Fortunately, XPath is quite simple and has a clearly
 * written <a href="http://www.w3.org/TR/xpath">specification </a>.</li>
 * <li><b>Feature Creation </b> <br>
 * FeatureType also must provide methods for the creation of Features, as specified in
 * FeatureFactory. The creating FeatureType should check to see if the passed in objects validate
 * against its AttributeTypes, and if it does should return a new Feature.</li>
 * </ol>
 * <h2>Redesign Notes (feature-exp2)</h2>
 * The main design goal of this is to have FeatureType extend AttributeType. This allows us to
 * nesting of features much more nicely. We already are going in this direction, with the
 * FeatureAttributeType buried in DefaultAttribute. This is just making it explicit, so it works
 * with the complex objects GML can return a lot more sensible. So much of the work in this class is
 * figuring out what concepts are the same. Some stuff may need to be rethought a bit, as there are
 * a few subtle assumptions that we are working with flat files. We will revisit this when we
 * implement choice and multiplicity.
 * <ul>
 * <li>got rid of deprecated getNamespace() method that returned a string, replaced it with a URI
 * return. This has been deprecated for a bit, and was done out of a desire to keep backwards
 * compatibility with 2.0, but that mission failed, so we're just moving on. This change will break
 * a few things, but is a good one, people just need to update their client code a bit.
 * <li>Deprecated getTypeName() to be getName(). They are the same thing, would be nice to get rid
 * of getTypeName, but it's used super extensively. Though perhaps we could consider keeping it as a
 * convenience, a bit more explicit, but it seems like overkill.
 * <li>Updated comments of the AttributeType operations that are inhierited to say what they mean
 * in the context of a FeatureType.
 * </ul>
 * <h2>Redesign Notes (factory-hints)</h2>
 * The factory-hints design is finally coming through on the promiss of the great geotools factory
 * design. This design is actualy placing the factory system under application (rather than
 * 'default') control, as such it is really showing every last place where we did not follow our
 * architecture.
 * <ul>
 * Use Cases:
 * <li>Custom Feature: <br>
 * Application spedcifies the use of a custom feature implementation. This is used so an application
 * interface is supported by each and every feature created.
 * <li>optiomized coordinate storage <br>
 * LiteRenderer2 wants Shape2d specific CoordinateSequenceFactory used for all Geometry creation.
 * The point is to allow only the xy information to be retrieved, and in a format suitable for rapid
 * reprojection and coversion to a Java2D Shape. Any OpenGL (or Java3D) based renderer would also
 * run into this need.
 * </ul>
 * The second use case is interesting in that LiteRenderer2 will be using the Datastore at the same
 * time as other threads that want the normal coordinate sequence. So this is a per Query hint.
 * </ul>
 * <p>
 * Consequence: Since FeatureType is immutable, and CoordianteSequence is specified by the
 * GeometryFactory of the DefaultGeometryAttribute this implys that we have a per Query SchemaType.
 * </p>
 * <p>
 * It strikes me that this is a bad separation of concerns the "schema" should be exactly the same,
 * it is just the GeometryFactory that controls construction that is in the wrong spot. It should be
 * a hint, not attached to GeomtryAttributeType.
 * </p>
 *
 * @author Rob Hranac, VFNY
 * @author Chris Holmes, TOPP
 * @author David Zwiers, Refractions
 * @author Jody Garnett, Refractions
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/api/src/main/java/org/geotools/feature/FeatureType.java $
 * @version $Id: FeatureType.java 27002 2007-09-17 03:01:53Z jdeolive $
 * @see org.geotools.feature.Feature
 * @see org.geotools.feature.FeatureTypeFactory
 * @see org.geotools.feature.type.NestedAttributeType
 * @see org.geotools.feature.DefaultFeatureType
 */
public interface FeatureType {
    //
    // XML Mapping Information
    //
    // Used for mapping Feature information to Geographical Markup Language,
    // 
    /**
     * Gets the global schema namespace.
     * <p>
     * This is often used to record the schema prefix (not the URI) required when writing out this
     * Feature using GML. The supporting application will need to be able to start the GMLWriter off
     * with the correct URI information so everything makes sense.
     * </p>
     * <p>
     * The Registry interface provided by the data module provides an example of how to store
     * associate FeatureType and namespace information. Please note that you may not have duplicate
     * typeNames in the same Namespace.
     * </p>
     *
     * @return Namespace of schema
     */
    URI getNamespace();

    /**
     * Gets the type name for this schema.
     * <p>
     * In GML this must be the element name of the Feature.
     * </p>
     *
     * @return The name of this feature type.
     */
    String getTypeName();

    /**
     * Test to determine whether this FeatureType is descended from the given FeatureType. Think of
     * this relationship likes the "extends" relationship in java.
     *
     * @param nsURI The namespace URI to use.
     * @param typeName The typeName.
     * @return true if descendant, false otherwise.
     */
    boolean isDescendedFrom(URI nsURI, String typeName);

    /**
     * A convenience method for calling <br>
     * <code><pre>
     * FeatureType f1;
     * FeatureType f2;
     * f1.isDescendedFrom(f2.getNamespace(), f2.getName());
     * </pre></code>
     * <p>
     * Question: this method duplicates the information provided by getAncestors().
     * </p>
     *
     * @param type The type to compare to.
     * @return true if descendant, false otherwise.
     */
    boolean isDescendedFrom(FeatureType type);

    /**
     * Is this FeatureType an abstract type?
     * <p>
     * When true is returned instances of this Feature cannot be created, instead a child
     * FeatureType must use this FeatureType as its ancestor.
     * </p>
     *
     * @return true if abstract, false otherwise.
     */
    boolean isAbstract();

    /**
     * Obtain an array of this FeatureType's direct ancestors.
     * Implementors should return a non-null array (may be of length 0).
     * <p>
     * <ul>
     * <li>length 0 - a root FeatureType
     * <li>length 1 - singe inhieratance
     * <li>length 1+ - multiple inheiratance, order is not significant
     * </ul>
     * </p>
     * GetAncestors() indicates *just* the direct parents of this FeatureType.
     * It capures the inheirtance relationship from the OGC overview document.
     * </p>
     * <p>
     * Example Code:<pre><code>
     * public FeatureType getAncestors()}
     *     return new FeatureType[]{ getSuper(), };
     * }
     * </code></pre>
     * </p>
     *
     * @return An array of ancestors.
     */
    FeatureType[] getAncestors();

    /**
     * Return the direct supertype of this FeatureType.
     * <p>
     * This is a conviance method for FeatureType where only single
     * inheirtance is supported (as is the case with GML).
     * </p>
     * <p>
     * This method will return null for a root FeatureType.
     * </p>
     * @return Get the super type, or null for root.
     */

    //FeatureType getSuper();

    /**
     * Gets the default geometry AttributeType.
     * <p>
     * If the FeatureType has more one geometry it is up to the implementor to determine which
     * geometry is the default. If working with multiple geometries it is best to get the
     * attributeTypes and iterate through them, checking for instances of GeometryAttribtueType.
     * </p>
     * <p>
     * This should just be used a convenience method when it is known that the features do not have
     * multiple geometries.
     * </p>
     *
     * @return The attribute type of the default geometry, which will contain the position.
     */
    GeometryAttributeType getDefaultGeometry();

    /**
     * The number of attribues defined by this schema.
     * <p>
     * This method to allows access to the complete schema as defined by this
     * FeatureType and its ancestors.
     * </p>
     * <p>
     * <ul>
     * Notes:
     * <li>
     * <li>for FeatureType with no super type this count value is the the
     *     same as getAttributeTypes().length.
     * </p>
     *
     * @return number of distinct attributeTypes available, taking ancestors and taking overrides
     *         into account.
     */
    int getAttributeCount();

    /**
     * Gets the attributeType at this xPath, if the specified attributeType does
     * not exist then <code>null</code> is returned.
     * <p>
     * Question: it is unclear how this interacts with the complete schema defined by this
     * FeatureType and its ancestors (in which a given xpath may refer to several AttributeTypes as
     * restrictions are applied.
     * </p>
     * <p>
     * Perhaps this method should be restricted to a FlatFeatureType? Or should have the option of
     * returning an array of matching AttributeType in order of inheiritence?
     * </p>
     *
     * @param xPath XPath pointer to attribute type.
     * @return AttributeType, or null if unavaialble
     */
    AttributeType getAttributeType(String xPath);

    /**
     * Find the position of a given AttributeType.
     * <p>
     * Match is based on attribute type name, the resulting index
     * may be used with getAttributeType( index ).
     * </p>
     * @param type The type to search for.
     * @return -1 if not found, a zero-based index if found.
     */
    int find(AttributeType type);

    /**
     * Find the position of an AttributeType which matches the given String.
     * <p>
     * This index may be used with getAttributeType( index ), the search space is the entire schema
     * defined by this FeatureType and its ancestors.
     * </p>
     *
     * @param attName the name to look for
     * @return -1 if not found, zero-based index otherwise
     */
    int find(String attName);

    /**
     * Gets the schema attributeType at the specified index.
     * <p>
     * The index is specified with respect to the entire Schema (as defined by this FeatureType and
     * it's ancestors).
     * <p>
     * <ul>
     * The index value should not be used with either:
     * <li>FeatureType.getAttributeTypes()[index] - as it defines only attributes contributed by
     * this FeatureType
     * <li>Feature.getAttribute( index ) - as attributes order may or may not be in sequence
     * </ul>
     * </p>
     *
     * @param position a integer index into the complete schema represented by
     *                 this FeatureType and its ancestors
     *
     * @return the attribute type at the specified position
     */
    AttributeType getAttributeType(int position);

    /**
     * AttributeTypes for this FeatureType, all attributes defined by this FeatureType
     * and its super types define schema for feature instances.
     * <p>
     * The provided array of AttributeTypes should be considered as adding to (or overriding) the
     * the AttribtueTypes defined by this FeatureTypes ancestors.
     * </p>
     * <p>
     * Note Well: Client code should not consider the index provided by the find( attName ) method
     * as a valid index into the returned array.
     * </p>
     *
     * @return Array of AttributeType describing this schema, array may be length 0, but should
     *         not be null
     */
    AttributeType[] getAttributeTypes();

    //
    // Depreicated AttributeType handling code
    //
    // This is origional Geotools 2.0 api that is depricated and will
    // be removed as part of Geotools 2.2.
    //
    /**
     * This is only used twice in the whole geotools code base, and one of those is for a test, so
     * we're removing it from the interface. If getAttributeType does not have the AttributeType it
     * will just return null. Gets the number of occurrences of this attribute.
     * <p>
     * Question: the comment says we are removing this, but it is not depricated? And how the heck
     * can the number of occurances out of a boolean.
     * </p>
     *
     * @deprecated It seems this method is ill concieved, use getAttributeType( xpath ) != null as a
     *             replacement
     * @param xPath XPath pointer to attribute type.
     * @return Number of occurrences.
     */
    boolean hasAttributeType(String xPath);

    //
    // This is code that was available in Geotools 2.0 as part of the
    // FeatureFactory API, In geotools 2.1 we are providing a clear
    // separation between schema information and construction.
    //
    /**
     * Create a duplicate of the provided feature, must delegate to an appropriate FeatureFactory
     * create method.
     * <p>
     * The implementation is assumed to make use of AttributeType duplicate as required for a deep
     * copy.
     * </p>
     *
     * //@deprecated This method will be removed in 2.2, please use FeatureFactory obtained from FactoryFinder
     * @param feature
     * @return a deep copy of feature
     * @throws IllegalAttributeException
     */
    Feature duplicate(Feature feature) throws IllegalAttributeException;

    /**
     * Creates a new feature, with a generated unique featureID.
     * <p>
     * Generating a FeatureID is less than ideal, as a FeatureID should be something
     * special about the "real world object" being modeled as a feature.
     * <p>
     * As an example: the "Effiel Tower" is a great feature id (there is only one) and we can
     * use that Feature ID in serveral systems:
     * <ul>
     * <li>In a tour guide FeatureTypes one which records (NAME, LOCATION, COST); and
     * <li>In a historical reference (NAME, LOCATION, HIEGHT, DATE, HISTORY)
     * </ul>
     * <p>
     * Explicitly a FeatureID should be persistant over time, and between systems.
     * <p>
     * The FeatureIDs "generated" by this method are generally created by a datasource.
     * This method is more for testing that doesn't need featureID.
     * </p>
     * <p>
     * Warning: This class does not follow the geotools guidelines of sepearting out
     * Interface from Factory, in geotools 2.3 there will be a formal FeatureFactory
     * allowing you to provide application specific FeatureImplementations as an orthogonal
     * concern to definition of type.
     * </p>
     *
     * @param attributes the array of attribute values
     * @return The created feature
     * @throws IllegalAttributeException if the FeatureType does not validate the attributes.
     */
    Feature create(Object[] attributes) throws IllegalAttributeException;

    /**
     * Scheduled for removal in Geotools 2.2, please use FeatureFactory.
     * <p>
     * Creates a new feature, with the indicated featureID.
     * </p>
     *
     * @param attributes the array of attribute values.
     * @param featureID the feature ID.
     * @return the created feature.
     * @throws IllegalAttributeException if the FeatureType does not validate the attributes.
     */
    Feature create(Object[] attributes, String featureID)
        throws IllegalAttributeException;

    /**
     * FeatureType check based on namespace, typeName, attributes and ancestors.
     * <p>
     * Conceptually FeatureType equality is supped to indicate an exact match based AttributeType
     * and Ancestors. The geotools models includes additional information of namespace
     * and typename.
     * </p>
     * <p>
     * As far as the geotools data model is concerned any FeatureType implementation should be able
     * to be subsituted for another. (An actually application may provided an implementation that
     * provdes tracking of metadata such as creation history, or support for additional interfaces).
     * </p>
     * <p>
     * Required implementation:<pre><code>
     * </code></pre>
     * A sample implementation is provided in FeatureTypes.equals( FeatureType, FeatureType ).
     * </p>
     * <p>
     * Questions:
     * <br>Q: You may wonder why namespace and typeName are not enought?
     * <br>A: Geotools often returns a "limitied" schema in response to a query where only a subset
     * of the attributes are requested.
     * <br>Q: Doesn't that still indicate the same schema?
     * <br>A: Yes it kind of should, a GML application (such as GeoServer) has to actually follow the
     * application schema and include attributes required by the schema (but not request by the query).
     * This library is more lax in its treatment, and expects you to uses isDecendedFrom as required.
     * </p>
     */
    public boolean equals(Object arg0);

    /**
     * Hascode based on namespace, typeName, attributes and ancestors.
     * <p>
     * Hascode is always a matched set with an equals implementation, please see the javadocs
     * for equals for a detailed discussion.
     * </p>
     * @see java.lang.Object#hashCode()
     */
    public int hashCode();
}
