/**
  * Get the data type of the command attribute
  *
  * @param wfs datastore
  * @return data type as Class
  */
 private Class<?> getDataType(WFSContentDataStore wfs) {
   SimpleFeatureType schema;
   Class<?> clazz = null;
   try {
     schema = wfs.getSchema(_wfsngTypeName); // get schema
     clazz = schema.getType(_command.getPropertyName()).getBinding(); // get data type as Class
   } catch (IOException e) {
     e.printStackTrace();
   }
   if (clazz == null) {
     throw new RuntimeException(
         "Should never happen, we need to know what type is the attribute of");
   }
   return clazz;
 }
  /**
   * Upload all the features from the WFS and then prepare the factories to fulfill the different
   * type of classifications and displays
   *
   * @throws DocServiceException
   */
  private void doClassification() throws DocServiceException {
    try {

      // connect to the remote WFS
      WFSContentDataStore wfs = connectToWFS(_command.getWFSUrl());

      // check if property name exists
      SimpleFeatureType ft = wfs.getSchema(_wfsngTypeName);
      int index = ft.indexOf(_command.getPropertyName());
      if (index == -1) {
        throw new DocServiceException(
            _command.getPropertyName() + " is not an attribute of " + _command.getFeatureTypeName(),
            HttpServletResponse.SC_BAD_REQUEST);
      }

      // Load all the features
      FeatureSource<SimpleFeatureType, SimpleFeature> source = wfs.getFeatureSource(_wfsngTypeName);
      FeatureCollection<SimpleFeatureType, SimpleFeature> featuresCollection = source.getFeatures();

      // We need a display (Symbolizers) and a value (Filters) fatories to generate a SLD file
      I_SymbolizerFactory symbolizerFact = null; // create symbols
      I_FilterFactory filterFact = null; // create filters

      // execute different type of classification given the type requested by user
      if (_command.getClassifType() == E_ClassifType.CHOROPLETHS
          || _command.getClassifType() == E_ClassifType.PROP_SYMBOLS) {

        // Classification on continuous values. Sorting is needed to classify:
        // Double values are mandatory (for now)

        if (getDataType(wfs) == String.class) {
          // choropleths and prop symbols use quantile classification
          // therefore classify on string type has no purpose
          throw new DocServiceException(
              "Classification on continous values ("
                  + _command.getClassifType()
                  + ").\n"
                  + "Attribute "
                  + _command.getPropertyName()
                  + " is string type."
                  + " Therefore no classification on contiuous values can be done."
                  + " It needs be a meaningful comparable type (numerical, date...)."
                  + " Use unique values classification instead.",
              HttpServletResponse.SC_BAD_REQUEST);
        } else if ((getDataType(wfs) != Double.class)
            && (getDataType(wfs) != Float.class)
            && (getDataType(wfs) != Integer.class)
            && (getDataType(wfs) != Long.class)
            && (getDataType(wfs) != Short.class)) {
          // for now, only double, float, integer, and short types are supported
          // FIXME deal with others numerical types, dates...
          // they all must be comparable type as sorting is required for classification
          throw new DocServiceException(
              "Classification on " + getDataType(wfs).getName() + " type is not supported.",
              HttpServletResponse.SC_NOT_IMPLEMENTED);
        }

        // get values to classify
        ArrayList<Double> values =
            getDoubleValues(featuresCollection.features(), _command.getPropertyName());
        filterFact =
            new ContinuousFilterFactory(
                values, _command.getClassCount(), _command.getPropertyName());

        if (_command.getClassifType() == E_ClassifType.CHOROPLETHS) {
          switch (_command.getSymbolType()) {
            case POLYGON:
              symbolizerFact =
                  new PolygonSymbolizerFactory(
                      _command.getClassCount(), _command.getFirstColor(), _command.getLastColor());
              break;
            case LINE:
              symbolizerFact =
                  new LineSymbolizerFactory(
                      _command.getClassCount(), _command.getFirstColor(), _command.getLastColor());
              break;
            case POINT:
              symbolizerFact =
                  new PointSymbolizerFactory(
                      _command.getClassCount(), _command.getFirstColor(), _command.getLastColor());
              break;
            default:
              throw new DocServiceException(
                  "Choropleths classification on symbol type: "
                      + _command.getSymbolType()
                      + " is not supported.",
                  HttpServletResponse.SC_BAD_REQUEST);
          }
        } else if (_command.getClassifType() == E_ClassifType.PROP_SYMBOLS) {
          switch (_command.getSymbolType()) {
            case LINE:
              symbolizerFact =
                  new LineSymbolizerFactory(
                      _command.getClassCount(), _command.getMinSize(), _command.getMaxSize());
              // customizing is possible
              // symbolizerFact.setColor(Color.BLUE);
              break;
            case POINT:
              symbolizerFact =
                  new PointSymbolizerFactory(
                      _command.getClassCount(), _command.getMinSize(), _command.getMaxSize());
              // customizing is possible
              // symbolizerFact.setColor(Color.BLUE);
              // symbolizerFact.setSymbol(StyleBuilder.MARK_CROSS);
              break;
            default:
              throw new DocServiceException(
                  "Proportional symbols classification on symbol type: "
                      + _command.getSymbolType()
                      + " is not supported.",
                  HttpServletResponse.SC_BAD_REQUEST);
          }
        }
      } else if (_command.getClassifType() == E_ClassifType.UNIQUE_VALUES) {

        // no needs to classify on Unique Values. They can be kept as Strings.
        Set<String> values =
            getUniqueStringValues(featuresCollection.features(), _command.getPropertyName());
        filterFact = new DiscreteFilterFactory(values, _command.getPropertyName());

        switch (_command.getSymbolType()) {
          case POLYGON:
            symbolizerFact = new PolygonSymbolizerFactory(_command.getPaletteID(), values.size());
            break;
          case LINE:
            symbolizerFact = new LineSymbolizerFactory(_command.getPaletteID(), values.size());
            break;
          case POINT:
            symbolizerFact = new PointSymbolizerFactory(_command.getPaletteID(), values.size());
            break;
          default:
            throw new DocServiceException(
                "Unique values classification on symbol type: "
                    + _command.getSymbolType()
                    + " is not supported.",
                HttpServletResponse.SC_BAD_REQUEST);
        }
      } else {
        throw new DocServiceException(
            "Unknown classification type: " + _command.getClassifType(),
            HttpServletResponse.SC_BAD_REQUEST);
      }

      assert (symbolizerFact != null);
      assert (filterFact != null);

      // With those 2 factories a FeatureTypeStyle can be created
      FeatureTypeStyle fts = createFeatureTypeStyle(filterFact, symbolizerFact);

      // Use FeatureTypeStyle to generate a complete SLD object
      _sld = createSLD(fts);
    } catch (IOException e) {
      e.printStackTrace(); // could happened when communicating with WFS
    }
  }