/* * 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 java.util.logging.Logger; 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.filter.expression.PropertyName; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.util.InternationalString; import com.vividsolutions.jts.geom.Geometry; /** * A builder for simple feature types. *
* Simple Usage: *
*
* //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();
*
*
*
* This builder builds type by maintaining state. Two types of state are maintained:
* Global Type State and Per Attribute State. Methods which set
* global state are named set<property>()
. Methods which set per attribute
* state are named <property>()
. Furthermore calls to per attribute
*
* * Global state is reset after a call to {@link #buildFeatureType()}. Per * attribute state is reset after a call to {@link #add}. *
*
* 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:
*
*
*
* builder.add( "pointProperty", Point.class );
* builder.add( "lineProperty", LineString.class );
* builder.add( "polygonProperty", Polygon.class );
*
* builder.setDefaultGeometry( "lineProperty" );
*
*
* The supplied coordinate reference is a "default" only used if geometric attributes are later * added to the type without specifying their coordinate reference system. *
* You must call this method prior to calling add for it to function. *
* Example: * *
*
* builder.setCRS( DefaultGeographicCRS.EGS84 );
* builder.add( "geom", Polygon.class );
*
*
*/
public void setCRS(CoordinateReferenceSystem crs) {
this.defaultCrs = crs;
this.defaultCrsSet = true; // set to true even if crs is null to avoid warning
}
/**
* 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 defaultCrs;
}
/**
* Sets the coordinate reference system of the next attribute being added 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.
*
* This method is used to associate an attribute type with a java class.
* The class is retreived from type.getBinding()
. 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.
*
* This value is reset after a call to {@link #add(String, Class)} *
*/ public SimpleFeatureTypeBuilder minOccurs( int minOccurs ) { attributeBuilder.setMinOccurs(minOccurs); return this; } /** * Sets the maxOccurs of the next attribute added to the feature type. ** This value is reset after a call to {@link #add(String, Class)} *
*/ public SimpleFeatureTypeBuilder maxOccurs( int maxOccurs ) { attributeBuilder.setMaxOccurs(maxOccurs); return this; } /** * Sets the nullability of the next attribute added to the feature type. ** This value is reset after a call to {@link #add(String, Class)} *
*/ 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. ** 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)} *
* @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. ** This value is reset after a call to {@link #add(String, Class)} *
*/ public SimpleFeatureTypeBuilder restriction( Filter filter ) { attributeBuilder.addRestriction( filter ); return this; } /** * Adds a collection of restrictions to the next attribute added to the ** This value is reset after a call to {@link #add(String, Class)} *
*/ public SimpleFeatureTypeBuilder restrictions( List* This value is reset after a call to {@link #add(String, Class)} *
*/ public SimpleFeatureTypeBuilder description( String description ) { attributeBuilder.setDescription( description ); return this; } /** * Sets the default value of the next attribute added to the feature type. ** This value is reset after a call to {@link #add(String, Class)} *
*/ public SimpleFeatureTypeBuilder defaultValue( Object defaultValue ) { attributeBuilder.setDefaultValue( defaultValue ); return this; } /** * Sets the crs of the next attribute added to the feature type. ** This only applies if the attribute added is geometric. *
* Example: builder.crs( DefaultGeographicCRS.WGS84 ).add( "geom", Polygon.class )
* * This value is reset after a call to {@link #add(String, Class)} *
*/ public SimpleFeatureTypeBuilder crs(CoordinateReferenceSystem crs) { attributeBuilder.setCRS(crs); return this; } /** * Sets the srs of the next attribute added to the feature type. ** The srs parameter is the id of a spatial reference system, for * example: "epsg:4326". *
** This only applies if the attribute added is geometric. *
** This value is reset after a call to {@link #add(String, Class)} *
* * @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. ** The srid parameter is the epsg code of a spatial reference * system, for example: "4326". *
** This only applies if the attribute added is geometric. *
** This value is reset after a call to {@link #add(String, Class)} *
* * @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. ** This value is reset after a call to {@link #add(String, Class)} *
* @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. *
* This method is convenience for:
*
* builder.minOccurs( descriptor.getMinOccurs() ).maxOccurs( descriptor.getMaxOccurs() )
* .nillable( descriptor.isNillable() )...
*
*
* The provided class is used to locate an attribute type binding previously specified by * {@link #addBinding(AttributeType)},{@link #addBindings(Schema)}, or * {@link #setBindings(Schema)}. *
** If not such binding exists then an attribute type is created on the fly. *
* * @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 geometry, 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 defaultCRS if (!attributeBuilder.isCRSSet()) { if( defaultCrs == null && !defaultCrsSet){ LOGGER.warning("Creating "+name+" with null CoordinateReferenceSystem - did you mean to setCRS?"); } attributeBuilder.setCRS(defaultCrs); } 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. ** Use of this method is discouraged. Consider using {@link #add(String, Class)}. *
*/ 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* Use of this method is discouraged. Consider using {@link #add(String, Class)}. *
*/ public void add( int index, AttributeDescriptor descriptor ) { attributes().add(index, descriptor); } /** * Adds a list of descriptors directly to the builder. ** Use of this method is discouraged. Consider using {@link #add(String, Class)}. *
*/ public void addAll( List* Use of this method is discouraged. Consider using {@link #add(String, Class)}. *
*/ public void addAll( AttributeDescriptor[] descriptors ) { if (descriptors != null) { for ( AttributeDescriptor ad : descriptors ) { add( ad ); } } } /** * Adds a new geometric attribute w/ provided name, class, and coordinate * reference system. *
* The crs parameter may be null
.
*
null
.
*/
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
*
* The srs parameter may be null
.
*
null
.
*/
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
*
* The srid parameter may be null
.
*
null
.
*/
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* After the type is built the running list of attributes is cleared. *
* @return The built feature type. */ public SimpleFeatureType buildFeatureType() { GeometryDescriptor defGeom = null; //was a default geometry set? if ( this.defaultGeometry != null ) { List* If the new copy can not be created reflectively.. {@link #newList()} is * returned. *
*/ @SuppressWarnings("rawtypes") protected List newList(List origional) { if (origional == null) { return newList(); } if (origional == Collections.EMPTY_LIST) { return newList(); } try { return origional.getClass().newInstance(); } catch (InstantiationException e) { return newList(); } catch (IllegalAccessException e) { return newList(); } } // Helper methods, // /** * Naming: Accessor which returns type name as follows: *typeName
has been set, its value is returned.
* name
has been set, it + namespaceURI
* are returned.
* * This method does a deep copy in that all individual attributes are copied as well. *
*/ public static SimpleFeatureType copy( SimpleFeatureType original ) { SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder(); //initialize the builder b.init( original ); //clear attributes b.attributes().clear(); //add attributes in order for( AttributeDescriptor descriptor : original.getAttributeDescriptors() ){ AttributeTypeBuilder ab = new AttributeTypeBuilder( b.factory ); ab.init( descriptor ); b.add( ab.buildDescriptor( descriptor.getLocalName() )); } return b.buildFeatureType(); } }