private FPolyline2D parseLineString(ByteBuffer data, boolean haveZ, boolean haveM) {
    FPoint2D[] points = parsePointArray(data, haveZ, haveM);
    GeneralPathX gp = new GeneralPathX();

    List<Double> d3 = null;

    int nDims = 2;
    if (gHaveZ || gHaveM) nDims = 3;

    if (nDims == 3) d3 = new ArrayList<Double>();

    for (int i = 0; i < points.length; i++) {
      // parent has 3 dimensions
      if (nDims == 3) {
        if (gHaveZ) d3.add(((FPoint3D) points[i]).getZ());
        else if (gHaveM) d3.add(((FPoint2DM) points[i]).getM());
        else d3.add(0.0); // child does not have 3 dimensions
      }
      if (i == 0) gp.moveTo(points[i].getX(), points[i].getY());
      else gp.lineTo(points[i].getX(), points[i].getY());
    }

    if (nDims == 3) {
      double ad3[] = new double[d3.size()];
      for (int i = 0; i < d3.size(); i++) {
        ad3[i] = ((Double) d3.get(i)).doubleValue();
      }

      if (gHaveZ) return new FPolyline3D(gp, ad3);
      else return new FPolyline2DM(gp, ad3);
    }
    return new FPolyline2D(gp);
  }
  // Its FShape and not FPolygon2D because FPolygon2DM returns a FPolyline
  private FShape parseMultiPolygon(ByteBuffer data) {
    // it was expected not to find polygons with different srid or
    // coordiante dimension as subelements of the multipolygon
    // PostGIS avoid this behaviour, but OGC says it is allow.

    int count = data.getInt();
    GeneralPathX gp = new GeneralPathX();

    List<Double> d3 = null;

    int nDims = 2;
    if (gHaveZ || gHaveM) nDims = 3;

    if (nDims == 3) d3 = new ArrayList<Double>();

    for (int i = 0; i < count; i++) {
      parseTypeAndSRID(data);
      int countRings = data.getInt();
      for (int j = 0; j < countRings; j++) {
        FPoint2D[] points = parsePointArray(data, gHaveZ, gHaveM);

        for (int k = 0; k < points.length; k++) {
          if (k == points.length - 1) {
            gp.closePath();
            if (nDims == 3) {
              if (gHaveZ) d3.add(((FPoint3D) points[0]).getZ());
              else if (gHaveM) d3.add(((FPoint2DM) points[0]).getM());
              else d3.add(0.0); // child does not have 3 dimensions
            }
          } else {
            // parent has 3 dimensions
            if (nDims == 3) {
              if (gHaveZ) d3.add(((FPoint3D) points[k]).getZ());
              else if (gHaveM) d3.add(((FPoint2DM) points[k]).getM());
              else d3.add(0.0); // child does not have 3 dimensions
            }
            if (k == 0) gp.moveTo(points[k].getX(), points[k].getY());
            else gp.lineTo(points[k].getX(), points[k].getY());
          }
        }
      }
    }

    if (nDims == 3) {
      double ad3[] = new double[d3.size()];
      for (int i = 0; i < d3.size(); i++) {
        ad3[i] = ((Double) d3.get(i)).doubleValue();
      }

      if (gHaveZ) return new FPolygon3D(gp, ad3);
      else return new FPolygon2DM(gp, ad3);
    }
    return new FPolygon2D(gp);
  }
  private FShape parsePolygon(ByteBuffer data, boolean haveZ, boolean haveM) {
    GeneralPathX gp = new GeneralPathX();

    List<Double> d3 = null;

    int nDims = 2;
    if (gHaveZ || gHaveM) nDims = 3;

    if (nDims == 3) d3 = new ArrayList<Double>();

    int countRings = data.getInt();
    for (int j = 0; j < countRings; j++) {
      FPoint2D[] points = parsePointArray(data, gHaveZ, gHaveM);
      for (int k = 0; k < points.length; k++) {
        if (k == points.length - 1) {
          gp.closePath();
          if (nDims == 3) {
            if (gHaveZ) d3.add(((FPoint3D) points[0]).getZ());
            else if (gHaveM) d3.add(((FPoint2DM) points[0]).getM());
            else d3.add(0.0); // child does not have 3 dimensions
          }
        } else {
          // parent has 3 dimensions
          if (nDims == 3) {
            if (gHaveZ) d3.add(((FPoint3D) points[k]).getZ());
            else if (gHaveM) d3.add(((FPoint2DM) points[k]).getM());
            else d3.add(0.0); // child does not have 3 dimensions
          }
          if (k == 0) gp.moveTo(points[k].getX(), points[k].getY());
          else gp.lineTo(points[k].getX(), points[k].getY());
        }
      }
    }

    if (nDims == 3) {
      double ad3[] = new double[d3.size()];
      for (int i = 0; i < d3.size(); i++) {
        ad3[i] = ((Double) d3.get(i)).doubleValue();
      }

      if (gHaveZ) return new FPolygon3D(gp, ad3);
      else return new FPolygon2DM(gp, ad3);
    }
    return new FPolygon2D(gp);
  }
  public void testExtendDangleFix() throws BaseException {

    GeneralPathX gpx = new GeneralPathX();
    gpx.moveTo(190, 200);
    gpx.lineTo(260, 130);
    gpx.lineTo(360, 140);
    gpx.lineTo(420, 180);
    gpx.lineTo(470, 220);
    gpx.lineTo(530, 240);
    FGeometry geometry = (FGeometry) ShapeFactory.createPolyline2D(gpx);

    GeneralPathX gpx2 = new GeneralPathX();
    gpx2.moveTo(860, 70);
    gpx2.lineTo(970, 120);
    gpx2.lineTo(1000, 220);
    gpx2.lineTo(920, 300);
    // gpx2.lineTo(470, 220); Cuando varios rectangulos contienen un mismo
    // punto,
    // el operador NNearest solo devuelve el primero que se insertara en el
    // indice
    gpx2.lineTo(1000, 370);
    FGeometry geometry2 = (FGeometry) ShapeFactory.createPolyline2D(gpx2);

    double searchRadius = 400d;

    Value[] values1 = new Value[1];
    values1[0] = ValueFactory.createValue(5d);

    Value[] values2 = new Value[1];
    values2[0] = ValueFactory.createValue(199d);

    DefaultFeature f1 = new DefaultFeature(geometry, values1, "0");
    DefaultFeature f2 = new DefaultFeature(geometry2, values2, "1");

    List<IFeature> features = new ArrayList<IFeature>();
    features.add(f1);
    features.add(f2);
    LayerDefinition def =
        new LayerDefinition() {
          public int getShapeType() {
            return FShape.LINE;
          }
        };
    def.setFieldsDesc(new FieldDescription[] {});
    FeatureCollectionMemoryDriver driver = new FeatureCollectionMemoryDriver("", features, def);
    FLyrVect lyr =
        (FLyrVect) com.iver.cit.gvsig.fmap.layers.LayerFactory.createLayer("", driver, null);

    Topology topo = new Topology(null, null, 0.2d, 0, new SimpleTopologyErrorContainer());

    LineMustNotHaveDangles violatedRule = new LineMustNotHaveDangles(topo, lyr, 0.1d);

    FGeometry dangleGeometry = (FGeometry) ShapeFactory.createPoint2D(530, 240);
    TopologyError error = new TopologyError(dangleGeometry, violatedRule, f1, topo);
    topo.addTopologyError(error);

    new ExtendDangleToNearestVertexFix(searchRadius).fix(error);

    assertTrue(topo.getNumberOfErrors() == 0);

    f1.setGeometry(geometry);
    topo.addTopologyError(error);

    new ExtendDangleToNearestBoundaryPointFix(searchRadius).fix(error);

    assertTrue(topo.getNumberOfErrors() == 0);
  }