/*
* 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.simple;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.geotools.feature.AttributeTypeBuilder;
import org.geotools.feature.NameImpl;
import org.geotools.feature.type.BasicFeatureTypes;
import org.geotools.feature.type.FeatureTypeFactoryImpl;
import org.geotools.referencing.CRS;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.AttributeType;
import org.opengis.feature.type.FeatureTypeFactory;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.feature.type.GeometryType;
import org.opengis.feature.type.Name;
import org.opengis.feature.type.Schema;
import org.opengis.filter.Filter;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.util.InternationalString;
import com.vividsolutions.jts.geom.Geometry;
/**
* A builder for simple feature types.
* <p>
* Simple Usage:
* <pre>
* <code>
* //create the builder
* SimpleTypeBuilder builder = new SimpleTypeBuilder();
*
* //set global state
* builder.setName( "testType" );
* builder.setNamespaceURI( "http://www.geotools.org/" );
* builder.setSRS( "EPSG:4326" );
*
* //add attributes
* builder.add( "intProperty", Integer.class );
* builder.add( "stringProperty", String.class );
* builder.add( "pointProperty", Point.class );
*
* //add attribute setting per attribute state
* builder.minOccurs(0).maxOccurs(2).nillable(false).add("doubleProperty",Double.class);
*
* //build the type
* SimpleFeatureType featureType = builder.buildFeatureType();
* </code>
* </pre>
* </p>
* This builder builds type by maintaining state. Two types of state are maintained:
* <i>Global Type State</i> and <i>Per Attribute State</i>. Methods which set
* global state are named <code>set<property>()</code>. Methods which set per attribute
* state are named <code><property>()</code>. Furthermore calls to per attribute
* </p>
* <p>
* Global state is reset after a call to {@link #buildFeatureType()}. Per
* attribute state is reset after a call to {@link #add}.
* </p>
* <p>
* A default geometry for the feature type can be specified explictly via
* {@link #setDefaultGeometry(String)}. However if one is not set the first
* geometric attribute ({@link GeometryType}) added will be resulting default.
* So if only specifying a single geometry for the type there is no need to
* call the method. However if specifying multiple geometries then it is good
* practice to specify the name of the default geometry type. For instance:
* <code>
* <pre>
* builder.add( "pointProperty", Point.class );
* builder.add( "lineProperty", LineString.class );
* builder.add( "polygonProperty", "polygonProperty" );
*
* builder.setDefaultGeometry( "lineProperty" );
* </pre>
* </code>
* </p>
*
* @author Justin Deolivera
* @author Jody Garnett
*
* @source $URL$
*/
public class SimpleFeatureTypeBuilder {
/**
* factories
*/
protected FeatureTypeFactory factory;
/**
* Map of java class bound to properties types.
*/
protected Map/* <Class,AttributeType> */bindings;
// Global state for the feature type
//
/**
* Naming: local name
*/
protected String local;
/**
* Naming: uri indicating scope
*/
protected String uri;
/**
* Description of type.
*/
protected InternationalString description;
/**
* List of attributes.
*/
protected List<AttributeDescriptor> attributes;
/**
* Additional restrictions on the type.
*/
protected List<Filter> restrictions;
/**
* Name of the default geometry to use
*/
protected String defaultGeometry;
/**
* coordinate reference system of the type
*/
protected CoordinateReferenceSystem crs;
/**
* flag controlling if the type is abstract.
*/
protected boolean isAbstract = false;
/**
* the parent type.
*/
protected SimpleFeatureType superType;
/**
* attribute builder
*/
protected AttributeTypeBuilder attributeBuilder;
/** Length for next filter */
private int length = -1;
/**
* Constructs the builder.
*/
public SimpleFeatureTypeBuilder() {
this( new FeatureTypeFactoryImpl() );
}
/**
* Constructs the builder specifying the factory for creating feature and
* feature collection types.
*/
public SimpleFeatureTypeBuilder(FeatureTypeFactory factory) {
this.factory = factory;
attributeBuilder = new AttributeTypeBuilder();
setBindings( new SimpleSchema() );
reset();
}
// Dependency Injection
//
/**
* Sets the factory used to create feature and feature collection types.
*/
public void setFeatureTypeFactory(FeatureTypeFactory factory) {
this.factory = factory;
}
/**
* The factory used to create feature and feature collection types.
*/
public FeatureTypeFactory getFeatureTypeFactory() {
return factory;
}
// Builder methods
//
/**
* Initializes the builder with state from a pre-existing feature type.
*/
public void init(SimpleFeatureType type) {
init();
if (type == null)
return;
uri = type.getName().getNamespaceURI();
local = type.getName().getLocalPart();
description = type.getDescription();
restrictions = null;
restrictions().addAll(type.getRestrictions());
attributes = null;
attributes().addAll(type.getAttributeDescriptors());
isAbstract = type.isAbstract();
superType = (SimpleFeatureType) type.getSuper();
}
/**
* Clears the running list of attributes.
*/
protected void init() {
attributes = null;
}
/**
* Completely resets all builder state.
*
*/
protected void reset() {
uri = BasicFeatureTypes.DEFAULT_NAMESPACE;
local = null;
description = null;
restrictions = null;
attributes = null;
crs = null;
isAbstract = false;
superType = BasicFeatureTypes.FEATURE;
}
/**
* Set the namespace uri of the built type.
*/
public void setNamespaceURI(String namespaceURI) {
this.uri = namespaceURI;
}
public void setNamespaceURI(URI namespaceURI) {
if ( namespaceURI != null ) {
setNamespaceURI( namespaceURI.toString() );
}
else {
setNamespaceURI( (String) null );
}
}
/**
* The namespace uri of the built type.
*/
public String getNamespaceURI() {
return uri;
}
/**
* Sets the name of the built type.
*/
public void setName(String name) {
this.local = name;
}
/**
* The name of the built type.
*/
public String getName() {
return local;
}
/**
* Sets the local name and namespace uri of the built type.
*/
public void setName(Name name) {
setName( name.getLocalPart() );
setNamespaceURI( name.getNamespaceURI() );
}
/**
* Sets the description of the built type.
*/
public void setDescription(InternationalString description) {
this.description = description;
}
/**
* The description of the built type.
*/
public InternationalString getDescription() {
return description;
}
/**
* Sets the name of the default geometry attribute of the built type.
*/
public void setDefaultGeometry(String defaultGeometryName) {
this.defaultGeometry = defaultGeometryName;
}
/**
* The name of the default geometry attribute of the built type.
*/
public String getDefaultGeometry() {
return defaultGeometry;
}
/**
* Sets the coordinate reference system of the built type.
* The supplied coordinate reference system is only used if
* geometric attributes are later added to the type without
* specifying their coordinate reference system.
*/
public void setCRS(CoordinateReferenceSystem crs) {
this.crs = crs;
}
/**
* The fallback coordinate reference system that will be applied to
* any geometric attributes added to the type without their own
* coordinate reference system specified.
*/
public CoordinateReferenceSystem getCRS() {
return crs;
}
/**
* Sets the coordinate reference system of the built type by specifying its
* srs.
*
* @throws IllegalArgumentException When the srs specified can be decored
* into a crs.
*
*/
public void setSRS(String srs) {
setCRS(decode(srs));
}
/**
* Sets the flag controlling if the resulting type is abstract.
*/
public void setAbstract(boolean isAbstract) {
this.isAbstract = isAbstract;
}
/**
* The flag controlling if the resulting type is abstract.
*/
public boolean isAbstract() {
return isAbstract;
}
/**
* Sets the super type of the built type.
*/
public void setSuperType(SimpleFeatureType superType) {
this.superType = superType;
}
/**
* The super type of the built type.
*/
public SimpleFeatureType getSuperType() {
return superType;
}
/**
* Specifies an attribute type binding.
* <p>
* This method is used to associate an attribute type with a java class.
* The class is retreived from <code>type.getBinding()</code>. When the
* {@link #add(String, Class)} method is used to add an attribute to the
* type being built, this binding is used to locate the attribute type.
* </p>
*
* @param type The attribute type.
*/
public void addBinding(AttributeType type) {
bindings().put(type.getBinding(), type);
}
/**
* Specifies a number of attribute type bindings.
*
* @param schema The schema containing the attribute types.
*
* @see {@link #addBinding(AttributeType)}.
*/
public void addBindings( Schema schema ) {
for (Iterator itr = schema.values().iterator(); itr.hasNext();) {
AttributeType type = (AttributeType) itr.next();
addBinding(type);
}
}
/**
* Specifies a number of attribute type bindings clearing out all existing
* bindings.
*
* @param schema The schema contianing attribute types.
*
* @see {@link #addBinding(AttributeType)}.
*/
public void setBindings( Schema schema ) {
bindings().clear();
addBindings( schema );
}
/**
* Looks up an attribute type which has been bound to a class.
*
* @param binding The class.
*
* @return AttributeType The bound attribute type.
*/
public AttributeType getBinding(Class<?> binding) {
return (AttributeType) bindings().get(binding);
}
// per attribute methods
//
/**
* Sets the minOccurs of the next attribute added to the feature type.
* <p>
* This value is reset after a call to {@link #add(String, Class)}
* </p>
*/
public SimpleFeatureTypeBuilder minOccurs( int minOccurs ) {
attributeBuilder.setMinOccurs(minOccurs);
return this;
}
/**
* Sets the maxOccurs of the next attribute added to the feature type.
* <p>
* This value is reset after a call to {@link #add(String, Class)}
* </p>
*/
public SimpleFeatureTypeBuilder maxOccurs( int maxOccurs ) {
attributeBuilder.setMaxOccurs(maxOccurs);
return this;
}
/**
* Sets the nullability of the next attribute added to the feature type.
* <p>
* This value is reset after a call to {@link #add(String, Class)}
* </p>
*/
public SimpleFeatureTypeBuilder nillable( boolean isNillable ) {
attributeBuilder.setNillable(isNillable);
return this;
}
/**
* Sets a restriction on the field length of the next attribute added to the feature type.
* <p>
* This method is the same as adding a restriction based on length( value ) < length
* This value is reset after a call to {@link #add(String, Class)}
* </p>
* @return length Used to limit the length of the next attribute created
*/
public SimpleFeatureTypeBuilder length( int length) {
attributeBuilder.setLength(length);
return this;
}
/**
* Adds a restriction to the next attribute added to the feature type.
* <p>
* This value is reset after a call to {@link #add(String, Class)}
* </p>
*/
public SimpleFeatureTypeBuilder restriction( Filter filter ) {
attributeBuilder.addRestriction( filter );
return this;
}
/**
* Adds a collection of restrictions to the next attribute added to the
* <p>
* This value is reset after a call to {@link #add(String, Class)}
* </p>
*/
public SimpleFeatureTypeBuilder restrictions( List<Filter> filters ) {
for ( Filter f : filters ) {
attributeBuilder.addRestriction(f);
}
return this;
}
/**
* Sets the description of the next attribute added to the feature type.
* <p>
* This value is reset after a call to {@link #add(String, Class)}
* </p>
*/
public SimpleFeatureTypeBuilder description( String description ) {
attributeBuilder.setDescription( description );
return this;
}
/**
* Sets the default value of the next attribute added to the feature type.
* <p>
* This value is reset after a call to {@link #add(String, Class)}
* </p>
*/
public SimpleFeatureTypeBuilder defaultValue( Object defaultValue ) {
attributeBuilder.setDefaultValue( defaultValue );
return this;
}
/**
* Sets the crs of the next attribute added to the feature type.
* <p>
* This only applies if the attribute added is geometric.
* </p>
* <p>
* This value is reset after a call to {@link #add(String, Class)}
* </p>
*/
public SimpleFeatureTypeBuilder crs( CoordinateReferenceSystem crs ) {
attributeBuilder.setCRS(crs);
return this;
}
/**
* Sets the srs of the next attribute added to the feature type.
* <p>
* The <tt>srs</tt> parameter is the id of a spatial reference system, for
* example: "epsg:4326".
* </p>
* <p>
* This only applies if the attribute added is geometric.
* </p>
* <p>
* This value is reset after a call to {@link #add(String, Class)}
* </p>
*
* @param srs The spatial reference system.
*/
public SimpleFeatureTypeBuilder srs( String srs ) {
if ( srs == null ) {
return crs( null );
}
return crs(decode(srs));
}
/**
* Sets the srid of the next attribute added to the feature type.
* <p>
* The <tt>srid</tt> parameter is the epsg code of a spatial reference
* system, for example: "4326".
* </p>
* <p>
* This only applies if the attribute added is geometric.
* </p>
* <p>
* This value is reset after a call to {@link #add(String, Class)}
* </p>
*
* @param srid The id of a spatial reference system.
*/
public SimpleFeatureTypeBuilder srid( Integer srid ) {
if ( srid == null ) {
return crs( null );
}
return crs( decode( "EPSG:" + srid ) );
}
/**
* Sets user data for the next attribute added to the feature type.
* <p>
* This value is reset after a call to {@link #add(String, Class)}
* </p>
* @param key The key of the user data.
* @param value The value of the user data.
*/
public SimpleFeatureTypeBuilder userData( Object key, Object value ) {
attributeBuilder.addUserData( key, value );
return this;
}
/**
* Sets all the attribute specific state from a single descriptor.
* <p>
* This method is convenience for:
* <code>
* builder.minOccurs( descriptor.getMinOccurs() ).maxOccurs( descriptor.getMaxOccurs() )
* .nillable( descriptor.isNillable() )...
* </code>
* </p>
*/
public SimpleFeatureTypeBuilder descriptor( AttributeDescriptor descriptor ) {
minOccurs( descriptor.getMinOccurs() );
maxOccurs( descriptor.getMaxOccurs() );
nillable( descriptor.isNillable() );
//namespaceURI( descriptor.getName().getNamespaceURI() );
defaultValue( descriptor.getDefaultValue() );
if ( descriptor instanceof GeometryDescriptor ) {
crs( ( (GeometryDescriptor) descriptor).getCoordinateReferenceSystem() );
}
return this;
}
/**
* Adds a new attribute w/ provided name and class.
*
* <p>
* The provided class is used to locate an attribute type binding previously
* specified by {@link #addBinding(AttributeType)},{@link #addBindings(Schema)},
* or {@link #setBindings(Schema)}.
* </p>
* <p>
* If not such binding exists then an attribute type is created on the fly.
* </p>
* @param name The name of the attribute.
* @param bind The class the attribute is bound to.
*
*/
public void add(String name, Class binding) {
AttributeDescriptor descriptor = null;
attributeBuilder.setBinding(binding);
attributeBuilder.setName(name);
//check if this is the name of the default geomtry, in that case we
// better make it a geometry type
//also check for jts geometry, if we ever actually get to a point where a
// feature can be backed by another geometry model (like iso), we need
// to remove this check
//
if ( ( defaultGeometry != null && defaultGeometry.equals( name ) )
|| Geometry.class.isAssignableFrom(binding) ) {
//if no crs was set, set to the global
if ( !attributeBuilder.isCRSSet() ) {
attributeBuilder.setCRS(crs);
}
GeometryType type = attributeBuilder.buildGeometryType();
descriptor = attributeBuilder.buildDescriptor(name, type);
}
else {
AttributeType type = attributeBuilder.buildType();
descriptor = attributeBuilder.buildDescriptor(name, type );
}
attributes().add(descriptor);
}
/**
* Adds a descriptor directly to the builder.
* <p>
* Use of this method is discouraged. Consider using {@link #add(String, Class)}.
* </p>
*/
public void add( AttributeDescriptor descriptor ) {
attributes().add(descriptor);
}
/**
* Removes an attribute from the builder
*
* @param attributeName the name of the AttributeDescriptor to remove
*
* @return the AttributeDescriptor with the name attributeName
* @throws IllegalArgumentException if there is no AttributeDescriptor with the name attributeName
*/
public AttributeDescriptor remove(String attributeName){
for (Iterator iterator = attributes.iterator(); iterator.hasNext();) {
AttributeDescriptor descriptor = (AttributeDescriptor) iterator.next();
if( descriptor.getLocalName().equals(attributeName) ){
iterator.remove();
return descriptor;
}
}
throw new IllegalArgumentException(attributeName+" is not an existing attribute descriptor in this builder");
}
/**
* Adds a descriptor to the builder by index.
* <p>
* Use of this method is discouraged. Consider using {@link #add(String, Class)}.
* </p>
*/
public void add( int index, AttributeDescriptor descriptor ) {
attributes().add(index, descriptor);
}
/**
* Adds a list of descriptors directly to the builder.
* <p>
* Use of this method is discouraged. Consider using {@link #add(String, Class)}.
* </p>
*/
public void addAll( List<AttributeDescriptor> descriptors ) {
if(descriptors != null)
for ( AttributeDescriptor ad : descriptors ) {
add( ad );
}
}
/**
* Adds an array of descriptors directly to the builder.
* <p>
* Use of this method is discouraged. Consider using {@link #add(String, Class)}.
* </p>
*/
public void addAll( AttributeDescriptor[] descriptors ) {
for ( AttributeDescriptor ad : descriptors ) {
add( ad );
}
}
/**
* Adds a new geometric attribute w/ provided name, class, and coordinate
* reference system.
* <p>
* The <tt>crs</tt> parameter may be <code>null</code>.
* </p>
* @param name The name of the attribute.
* @param binding The class that the attribute is bound to.
* @param crs The crs of of the geometry, may be <code>null</code>.
*/
public void add(String name, Class binding, CoordinateReferenceSystem crs ) {
attributeBuilder.setBinding(binding);
attributeBuilder.setName(name);
attributeBuilder.setCRS(crs);
GeometryType type = attributeBuilder.buildGeometryType();
GeometryDescriptor descriptor = attributeBuilder.buildDescriptor(name,type);
attributes().add(descriptor);
}
/**
* Adds a new geometric attribute w/ provided name, class, and spatial
* reference system identifier
* <p>
* The <tt>srs</tt> parameter may be <code>null</code>.
* </p>
* @param name The name of the attribute.
* @param binding The class that the attribute is bound to.
* @param srs The srs of of the geometry, may be <code>null</code>.
*/
public void add(String name, Class binding, String srs) {
if ( srs == null ) {
add(name,binding,(CoordinateReferenceSystem)null);
return;
}
add(name,binding,decode(srs));
}
/**
* Adds a new geometric attribute w/ provided name, class, and spatial
* reference system identifier
* <p>
* The <tt>srid</tt> parameter may be <code>null</code>.
* </p>
* @param name The name of the attribute.
* @param binding The class that the attribute is bound to.
* @param srid The srid of of the geometry, may be <code>null</code>.
*/
public void add(String name, Class binding, Integer srid) {
if ( srid == null ) {
add(name,binding,(CoordinateReferenceSystem)null);
return;
}
add( name, binding, decode( "EPSG:" + srid ) );
}
/**
* Directly sets the list of attributes.
* @param attributes the new list of attributes, or null to reset the list
*/
public void setAttributes(List<AttributeDescriptor> attributes) {
List<AttributeDescriptor> atts = attributes();
atts.clear();
if(attributes != null)
atts.addAll(attributes);
}
/**
* Directly sets the list of attributes.
* @param attributes the new list of attributes, or null to reset the list
*/
public void setAttributes(AttributeDescriptor[] attributes) {
List<AttributeDescriptor> atts = attributes();
atts.clear();
if(attributes != null)
atts.addAll(Arrays.asList(attributes));
}
/**
* Builds a feature type from compiled state.
* <p>
* After the type is built the running list of attributes is cleared.
* </p>
* @return The built feature type.
*/
public SimpleFeatureType buildFeatureType() {
GeometryDescriptor defGeom = null;
//was a default geometry set?
if ( this.defaultGeometry != null ) {
List<AttributeDescriptor> atts = attributes();
for ( int i = 0; i < atts.size(); i++) {
AttributeDescriptor att = atts.get(i);
if ( this.defaultGeometry.equals( att.getName().getLocalPart() ) ) {
//ensure the attribute is a geometry attribute
if ( !(att instanceof GeometryDescriptor ) ) {
attributeBuilder.init( att );
attributeBuilder.setCRS(crs);
GeometryType type = attributeBuilder.buildGeometryType();
att = attributeBuilder.buildDescriptor(att.getName(),type);
atts.set( i, att );
}
defGeom = (GeometryDescriptor)att;
break;
}
}
if (defGeom == null) {
String msg = "'" + this.defaultGeometry + " specified as default" +
" but could find no such attribute.";
throw new IllegalArgumentException( msg );
}
}
if ( defGeom == null ) {
//none was set by name, look for first geometric type
for ( AttributeDescriptor att : attributes() ) {
if ( att instanceof GeometryDescriptor ) {
defGeom = (GeometryDescriptor) att;
break;
}
}
}
SimpleFeatureType built = factory.createSimpleFeatureType(
name(), attributes(), defGeom, isAbstract,
restrictions(), superType, description);
init();
return built;
}
// Internal api available for subclasses to override
/**
* Creates a new set instance, this default implementation returns {@link HashSet}.
*/
protected Set newSet(){
return new HashSet();
}
/**
* Creates a new list instance, this default impelementation returns {@link ArrayList}.
*/
protected List newList() {
return new ArrayList();
}
/**
* Creates a new map instance, this default implementation returns {@link HashMap}
*/
protected Map newMap() {
return new HashMap();
}
/**
* Creates a new list which is the same type as the provided list.
* <p>
* If the new copy can not be created reflectively.. {@link #newList()} is
* returned.
* </p>
*/
protected List newList(List origional) {
if (origional == null) {
return newList();
}
if (origional == Collections.EMPTY_LIST) {
return newList();
}
try {
return (List) origional.getClass().newInstance();
} catch (InstantiationException e) {
return newList();
} catch (IllegalAccessException e) {
return newList();
}
}
// Helper methods,
//
/**
* Naming: Accessor which returns type name as follows:
* <ol>
* <li>If <code>typeName</code> has been set, its value is returned.
* <li>If <code>name</code> has been set, it + <code>namespaceURI</code>
* are returned.
* </ol>
*
*/
protected Name name() {
if (local == null)
return null;
return new NameImpl(uri, local);
}
/**
* Accessor for attributes.
*/
protected List<AttributeDescriptor> attributes() {
if (attributes == null) {
attributes = newList();
}
return attributes;
}
/**
* Accessor for restrictions.
*/
protected List<Filter> restrictions(){
if (restrictions == null) {
restrictions = newList();
}
return restrictions;
}
/**
* Accessor for bindings.
*/
protected Map bindings() {
if (bindings == null) {
bindings = newMap();
}
return bindings;
}
/**
* Decodes a srs, supplying a useful error message if there is a problem.
*/
protected CoordinateReferenceSystem decode( String srs ) {
try {
return CRS.decode(srs);
} catch (Exception e) {
String msg = "SRS '" + srs + "' unknown:" + e.getLocalizedMessage();
throw (IllegalArgumentException) new IllegalArgumentException( msg ).initCause( e );
}
}
/**
* Create a SimpleFeatureType containing just the descriptors indicated.
* @param original SimpleFeatureType
* @param types name of types to include in result
* @return SimpleFeatureType containing just the types indicated by name
*/
public static SimpleFeatureType retype( SimpleFeatureType original, String[] types ) {
SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder();
//initialize the builder
b.init( original );
//clear the attributes
b.attributes().clear();
//add attributes in order
for ( int i = 0; i < types.length; i++ ) {
b.add( original.getDescriptor( types[i] ) );
}
return b.buildFeatureType();
}
/**
* Create a SimpleFeatureType with the same content; just updating the geometry
* attribute to match the provided coordinate reference system.
* @param original SimpleFeatureType
* @param crs CoordianteReferenceSystem of result
* @return SimpleFeatureType updated with the provided CoordinateReferenceSystem
*/
public static SimpleFeatureType retype( SimpleFeatureType original,CoordinateReferenceSystem crs ) {
SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder();
//initialize the builder
b.init( original );
//clear the attributes
b.attributes().clear();
//add attributes in order
for( AttributeDescriptor descriptor : original.getAttributeDescriptors() ){
if( descriptor instanceof GeometryDescriptor ){
GeometryDescriptor geometryDescriptor = (GeometryDescriptor) descriptor;
AttributeTypeBuilder adjust = new AttributeTypeBuilder( b.factory );
adjust.init( geometryDescriptor );
adjust.setCRS( crs );
b.add( adjust.buildDescriptor( geometryDescriptor.getLocalName() ));
continue;
}
b.add( descriptor);
}
return b.buildFeatureType();
}
}