/**
   * Creates a query that requests the features in sourceLayer as dictated by filter. Query only
   * requests the attributes that can be mapped from sourceLayer to targetLayer.
   *
   * @param queryAttributes populates with a mapping of attributeTypeNames from targetLayer to
   *     sourcelayer
   * @return
   */
  @SuppressWarnings("unchecked")
  private Query createQuery(
      ILayer sourceLayer, Filter filter, Layer targetLayer, Map<String, String> queryAttributes) {
    SimpleFeatureType sourceSchema = sourceLayer.getSchema();
    SimpleFeatureType targetSchema = targetLayer.getSchema();
    // Maps type names to type names since we are ignoring case

    queryAttributes.putAll(FeatureUtils.createAttributeMapping(sourceSchema, targetSchema));
    Set<String> properties = new HashSet(queryAttributes.values());
    return new DefaultQuery(
        sourceSchema.getName().getLocalPart(),
        filter,
        properties.toArray(new String[properties.size()]));
  }
  @SuppressWarnings("unchecked")
  private void copyFeatures(
      ILayer sourceLayer, Filter filter, final Layer targetLayer, final IProgressMonitor monitor) {
    monitor.beginTask(Messages.CopyFeaturesCommand_name, 104);
    final int[] worked = new int[] {-3};
    monitor.worked(1);
    try {
      SubProgressMonitor subProgressMonitor = new SubProgressMonitor(monitor, 2);
      FeatureStore<SimpleFeatureType, SimpleFeature> destination =
          targetLayer.getResource(FeatureStore.class, subProgressMonitor);
      subProgressMonitor.done();
      worked[0] += 2;
      subProgressMonitor = new SubProgressMonitor(monitor, 2);
      FeatureSource<SimpleFeatureType, SimpleFeature> source =
          sourceLayer.getResource(FeatureSource.class, subProgressMonitor);
      subProgressMonitor.done();
      worked[0] += 2;
      // If no FeatureStore then features can't be copied
      // If no FeatureSource then features can't be copied
      if (destination == null || source == null) {
        targetLayer.setFilter(filter);
        return;
      }

      // Create a query that will get the attributes that are compatible
      // what is compatible? Do the attribute names and types have to be the same or
      // just the types.
      // Executive decision:
      // Match both name and type, the rest must be customized.
      final HashMap<String, String> attributeMap = new HashMap<String, String>();
      Query query = createQuery(sourceLayer, filter, targetLayer, attributeMap);
      if (attributeMap.isEmpty()) {
        targetLayer.setFilter(filter);
        return;
      }
      MathTransform mt = createMathTransform(sourceLayer, targetLayer);
      FeatureCollection<SimpleFeatureType, SimpleFeature> features = source.getFeatures(query);
      SimpleFeatureType schema = targetLayer.getSchema();

      CopyFeatureCollection c =
          new CopyFeatureCollection(
              schema,
              features,
              monitor,
              worked,
              mt,
              attributeMap,
              targetLayer.layerToMapTransform());
      Envelope env = c.env;
      targetLayer.eSetDeliver(false);
      try {
        List<FeatureId> fids = destination.addFeatures(c);

        displayCopiedFeatures(env);

        FilterFactory filterFactory =
            CommonFactoryFinder.getFilterFactory(GeoTools.getDefaultHints());
        addedFeaturesFilter = filterFactory.id(new HashSet(fids));
      } finally {
        targetLayer.eSetDeliver(true);
      }
      getMap().getRenderManager().refresh(targetLayer, env);
    } catch (IOException e) {
      throw (RuntimeException) new RuntimeException().initCause(e);
    } finally {
      monitor.done();
    }
  }