@Override
  protected SimpleFeatureType buildFeatureType() {
    String[] headers;
    Map<String, Class<?>> typesFromData;
    CsvReader csvReader = null;
    try {
      csvReader = csvFileState.openCSVReader();
      headers = csvReader.getHeaders();
      typesFromData = CSVStrategy.findMostSpecificTypesFromData(csvReader, headers);
    } catch (IOException e) {
      throw new RuntimeException(e);
    } finally {
      if (csvReader != null) {
        csvReader.close();
      }
    }
    SimpleFeatureTypeBuilder builder =
        CSVStrategy.createBuilder(csvFileState, headers, typesFromData);
    Class<?> latClass = typesFromData.get(latField);
    Class<?> lngClass = typesFromData.get(lngField);
    if (CSVStrategy.isNumeric(latClass) && CSVStrategy.isNumeric(lngClass)) {
      List<String> csvHeaders = Arrays.asList(headers);
      int index = csvHeaders.indexOf(latField);
      AttributeTypeBuilder builder2 = new AttributeTypeBuilder();
      builder2.setCRS(DefaultGeographicCRS.WGS84);
      builder2.binding(Point.class);
      AttributeDescriptor descriptor = builder2.buildDescriptor(pointField);
      builder.add(index, descriptor);

      builder.remove(latField);
      builder.remove(lngField);
    }
    return builder.buildFeatureType();
  }
  static AttributeDescriptor getIntersectionType(
      SimpleFeatureCollection first, SimpleFeatureCollection second) {
    Class firstGeomType = first.getSchema().getGeometryDescriptor().getType().getBinding();
    Class secondGeomType = second.getSchema().getGeometryDescriptor().getType().getBinding();

    // figure out the output geometry type
    Class binding;
    if (isGeometryTypeIn(secondGeomType, Point.class)) {
      binding = Point.class;
    } else if (isGeometryTypeIn(secondGeomType, MultiPoint.class)) {
      binding = MultiPoint.class;
    } else if (isGeometryTypeIn(secondGeomType, LineString.class, MultiLineString.class)) {
      binding = MultiLineString.class;
    } else if (isGeometryTypeIn(secondGeomType, Polygon.class, MultiPolygon.class)) {
      if (isGeometryTypeIn(firstGeomType, Polygon.class, MultiPolygon.class)) {
        binding = MultiPolygon.class;
      } else {
        binding = MultiLineString.class;
      }
    } else {
      // we can't be more precise than this
      binding = Geometry.class;
    }

    AttributeTypeBuilder builder = new AttributeTypeBuilder();
    builder.setName("the_geom");
    builder.setBinding(binding);
    builder.setCRS(first.features().next().getFeatureType().getCoordinateReferenceSystem());
    AttributeDescriptor descriptor = builder.buildDescriptor("the_geom");
    return descriptor;
  }
  /**
   * This method joins the feature type with the types maintained by this object. Geometry attribute
   * is not added into the join geometry. It must be computed by the client and setted using the
   * setGeometry Method.
   *
   * @see setGeometry
   * @param featureType
   */
  public FeatureTypeUnionBuilder add(final SimpleFeatureType featureType) {

    // adds the attribute types of this feature type, if there are name
    // collisions
    // the method appends the number "2" at the name to avoid name
    // duplication.
    // The geometry attribute will be omitted.
    for (int i = 0; i < featureType.getAttributeCount(); i++) {

      AttributeDescriptor attributeType = featureType.getDescriptor(i);
      if (!(attributeType instanceof GeometryDescriptor)) {

        String attrUnionName = attributeType.getLocalName();
        if (this.mapUnionAttributes.containsKey(attrUnionName)) {
          StringBuffer duplicatedName = new StringBuffer(attrUnionName);
          duplicatedName.append("2");

          attrUnionName = duplicatedName.toString();
        }
        AttributeTypeBuilder builder = new AttributeTypeBuilder();
        builder.setBinding(attributeType.getType().getBinding());
        builder.setNillable(attributeType.isNillable());

        AttributeDescriptor newAttribute = builder.buildDescriptor(attrUnionName);

        mapUnionAttributes.put(attrUnionName, newAttribute);
        mapOriginalUnion.put(
            new UnionKey(featureType.getTypeName(), attributeType.getLocalName()), attrUnionName);
      }
    }
    return this;
  }
  /**
   * Adds a new geometric attribute w/ provided name, class, and coordinate reference system.
   *
   * <p>The <tt>crs</tt> parameter may be <code>null</code>.
   *
   * @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);
  }
    private void collectAttributes(
        SimpleFeatureType schema, List<String> retainedAttributes, SimpleFeatureTypeBuilder tb) {
      for (AttributeDescriptor descriptor : schema.getAttributeDescriptors()) {
        // check whether descriptor has been selected in the attribute list
        boolean isInRetainList = true;
        if (retainedAttributes != null) {

          isInRetainList = retainedAttributes.contains(descriptor.getLocalName());
          logger.fine("Checking " + descriptor.getLocalName() + " --> " + isInRetainList);
        }
        if (!isInRetainList || schema.getGeometryDescriptor() == descriptor) {
          continue;
        }

        // build the attribute to return
        AttributeTypeBuilder builder = new AttributeTypeBuilder();
        builder.setName(schema.getName().getLocalPart() + "_" + descriptor.getName());
        builder.setNillable(descriptor.isNillable());
        builder.setBinding(descriptor.getType().getBinding());
        builder.setMinOccurs(descriptor.getMinOccurs());
        builder.setMaxOccurs(descriptor.getMaxOccurs());
        builder.setDefaultValue(descriptor.getDefaultValue());
        builder.setCRS(schema.getCoordinateReferenceSystem());
        AttributeDescriptor intersectionDescriptor =
            builder.buildDescriptor(
                schema.getName().getLocalPart() + "_" + descriptor.getName(), descriptor.getType());
        tb.add(intersectionDescriptor);
        tb.addBinding(descriptor.getType());
      }
    }
  private static SimpleFeatureType getPointSimpleFeatureType() {

    final String NAME = "PointSimpleFeatureType";
    final SimpleFeatureTypeBuilder sftBuilder = new SimpleFeatureTypeBuilder();
    final AttributeTypeBuilder atBuilder = new AttributeTypeBuilder();
    sftBuilder.setName(NAME);
    sftBuilder.add(atBuilder.binding(String.class).nillable(false).buildDescriptor("locationName"));
    sftBuilder.add(atBuilder.binding(Geometry.class).nillable(false).buildDescriptor("geometry"));

    return sftBuilder.buildFeatureType();
  }
  @Test
  public void testCreateBasedOnShapeType() throws Exception {
    TestEditBlackboard bb = new TestEditBlackboard();
    String fid = "FeatureID";
    EditGeom geom = bb.newGeom(fid, null);
    bb.addPoint(10, 10, geom.getShell());
    EditGeom geom2 = bb.newGeom(fid, null);
    AttributeTypeBuilder builder = new AttributeTypeBuilder();
    builder.setBinding(Geometry.class);
    builder.setName("geom");
    GeometryDescriptor at = builder.buildDescriptor("geom", builder.buildGeometryType());
    bb.addPoint(100, 100, geom2.getShell());

    String fid2 = "FID2";
    EditGeom differentGeom = bb.newGeom(fid2, null);
    bb.addPoint(200, 200, differentGeom.getShell());

    Map<String, Bag> result = GeometryCreationUtil.createAllGeoms(geom, Point.class, at, false);
    assertEquals(1, result.get(fid2).jts.size());
    assertEquals(Point.class, result.get(fid2).jts.get(0).getClass());

    differentGeom.setShapeType(ShapeType.LINE);
    result = GeometryCreationUtil.createAllGeoms(geom, Point.class, at, false);
    assertEquals(1, result.get(fid2).jts.size());
    assertEquals(LineString.class, result.get(fid2).jts.get(0).getClass());

    differentGeom.setShapeType(ShapeType.POLYGON);
    result = GeometryCreationUtil.createAllGeoms(geom, Point.class, at, false);
    assertEquals(1, result.get(fid2).jts.size());
    assertEquals(Polygon.class, result.get(fid2).jts.get(0).getClass());

    differentGeom.setShapeType(ShapeType.UNKNOWN);
    result = GeometryCreationUtil.createAllGeoms(geom, Point.class, at, false);
    assertEquals(1, result.get(fid2).jts.size());
    assertEquals(Point.class, result.get(fid2).jts.get(0).getClass());

    bb.addPoint(200, 200, differentGeom.getShell());

    result = GeometryCreationUtil.createAllGeoms(geom, Point.class, at, false);
    assertEquals(1, result.get(fid2).jts.size());
    assertEquals(Point.class, result.get(fid2).jts.get(0).getClass());

    bb.addPoint(200, 210, differentGeom.getShell());

    result = GeometryCreationUtil.createAllGeoms(geom, Point.class, at, false);
    assertEquals(1, result.get(fid2).jts.size());
    assertEquals(LineString.class, result.get(fid2).jts.get(0).getClass());

    bb.addPoint(200, 200, differentGeom.getShell());

    result = GeometryCreationUtil.createAllGeoms(geom, Point.class, at, false);
    assertEquals(1, result.get(fid2).jts.size());
    assertEquals(Polygon.class, result.get(fid2).jts.get(0).getClass());
  }
  /**
   * Builds a feature type from compiled state.
   *
   * <p>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<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)) {
            LOGGER.warning("Default Geometry " + this.defaultGeometry + " was added as a geoemtry");
            attributeBuilder.init(att);
            attributeBuilder.setCRS(defaultCrs);
            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;
  }
  public FeatureTypeUnionBuilder setGeometryClass(
      final String geometryName,
      final Class<? extends Geometry> geomClass,
      final CoordinateReferenceSystem crs) {

    assert geometryName != null : "the geometry name can not be null";
    assert geomClass != null : "the geometry class can not be null";
    assert crs != null : "the CRS can not be null";

    this.geometryName = geometryName;
    this.geometryClass = geomClass;

    GeometryDescriptor geoAttrType;

    AttributeTypeBuilder build = new AttributeTypeBuilder();
    build.setName(this.geometryName);
    build.setBinding(this.geometryClass);
    build.setNillable(true);
    build.setLength(100);
    build.setCRS(crs);

    GeometryType type = build.buildGeometryType();
    geoAttrType = build.buildDescriptor(this.geometryName, type);

    this.unionGeometryAttr = geoAttrType;

    return this;
  }
  /**
   * Copys a feature type.
   *
   * <p>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();
  }
  /**
   * Create a SimpleFeatureType with the same content; just updating the geometry attribute to match
   * the provided coordinate reference system.
   *
   * @param original SimpleFeatureType
   * @param crs CoordinateReferenceSystem 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();
  }
  /**
   * 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>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.fine(
              "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);
  }
  public static SimpleFeatureType createSubType(
      SimpleFeatureType featureType,
      String[] properties,
      CoordinateReferenceSystem override,
      String typeName,
      URI namespace)
      throws SchemaException {

    if ((properties == null) && (override == null)) {
      return featureType;
    }

    if (properties == null) {
      properties = new String[featureType.getAttributeCount()];
      for (int i = 0; i < properties.length; i++) {
        properties[i] = featureType.getDescriptor(i).getLocalName();
      }
    }

    String namespaceURI = namespace != null ? namespace.toString() : null;
    boolean same =
        featureType.getAttributeCount() == properties.length
            && featureType.getTypeName().equals(typeName)
            && Utilities.equals(featureType.getName().getNamespaceURI(), namespaceURI);

    for (int i = 0; (i < featureType.getAttributeCount()) && same; i++) {
      AttributeDescriptor type = featureType.getDescriptor(i);
      same =
          type.getLocalName().equals(properties[i])
              && (((override != null) && type instanceof GeometryDescriptor)
                  ? assertEquals(
                      override, ((GeometryDescriptor) type).getCoordinateReferenceSystem())
                  : true);
    }

    if (same) {
      return featureType;
    }

    AttributeDescriptor[] types = new AttributeDescriptor[properties.length];

    for (int i = 0; i < properties.length; i++) {
      types[i] = featureType.getDescriptor(properties[i]);

      if ((override != null) && types[i] instanceof GeometryDescriptor) {
        AttributeTypeBuilder ab = new AttributeTypeBuilder();
        ab.init(types[i]);
        ab.setCRS(override);
        types[i] = ab.buildDescriptor(types[i].getLocalName(), ab.buildGeometryType());
      }
    }

    if (typeName == null) typeName = featureType.getTypeName();
    if (namespace == null && featureType.getName().getNamespaceURI() != null)
      try {
        namespace = new URI(featureType.getName().getNamespaceURI());
      } catch (URISyntaxException e) {
        throw new RuntimeException(e);
      }

    SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder();
    tb.setName(typeName);
    tb.setNamespaceURI(namespace);
    tb.addAll(types);

    return tb.buildFeatureType();
  }
 /**
  * Replace the descriptor at the provided index.
  *
  * @param index
  * @param descriptor
  */
 public void set(String attributeName, AttributeTypeBuilder attributeBuilder) {
   AttributeDescriptor descriptor = attributeBuilder.buildDescriptor(attributeName);
   set(attributeName, descriptor);
 }
 /**
  * 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)}
  */
 public SimpleFeatureTypeBuilder maxOccurs(int maxOccurs) {
   attributeBuilder.setMaxOccurs(maxOccurs);
   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)}
  *
  * @return length Used to limit the length of the next attribute created
  */
 public SimpleFeatureTypeBuilder length(int length) {
   attributeBuilder.setLength(length);
   return this;
 }
 /**
  * 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)}
  *
  * @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 the crs of the next attribute added to the feature type.
  *
  * <p>This only applies if the attribute added is geometric. Example: <code>
  *  builder.crs( DefaultGeographicCRS.WGS84 ).add( "geom", Polygon.class )</code>
  *
  * <p>This value is reset after a call to {@link #add(String, Class)}
  */
 public SimpleFeatureTypeBuilder crs(CoordinateReferenceSystem crs) {
   attributeBuilder.setCRS(crs);
   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)}
  */
 public SimpleFeatureTypeBuilder description(String description) {
   attributeBuilder.setDescription(description);
   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)}
  */
 public SimpleFeatureTypeBuilder restrictions(List<Filter> filters) {
   for (Filter f : filters) {
     attributeBuilder.addRestriction(f);
   }
   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)}
  */
 public SimpleFeatureTypeBuilder restriction(Filter filter) {
   attributeBuilder.addRestriction(filter);
   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)}
  */
 public SimpleFeatureTypeBuilder defaultValue(Object defaultValue) {
   attributeBuilder.setDefaultValue(defaultValue);
   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)}
  */
 public SimpleFeatureTypeBuilder nillable(boolean isNillable) {
   attributeBuilder.setNillable(isNillable);
   return this;
 }