public void skipNestedMapping(AttributeMapping attMapping, List<Feature> sources)
      throws IOException {
    if (attMapping instanceof JoiningNestedAttributeMapping) {

      for (Feature source : sources) {
        Object value =
            getValues(attMapping.isMultiValued(), attMapping.getSourceExpression(), source);

        if (value instanceof Collection) {
          for (Object val : (Collection) value) {
            ((JoiningNestedAttributeMapping) attMapping).skip(this, val, getIdValues(source));
          }
        } else {
          ((JoiningNestedAttributeMapping) attMapping).skip(this, value, getIdValues(source));
        }
      }
    }
  }
  protected void closeSourceFeatures() {
    if (sourceFeatures != null && getSourceFeatureIterator() != null) {
      sourceFeatureIterator.close();
      sourceFeatureIterator = null;
      sourceFeatures = null;
      filteredFeatures = null;
      listFilter = null;

      // NC - joining nested atts
      for (AttributeMapping attMapping : selectedMapping) {
        if (attMapping instanceof JoiningNestedAttributeMapping) {
          ((JoiningNestedAttributeMapping) attMapping).close(this);
        }
      }
    }
  }
  protected void initialiseSourceFeatures(
      FeatureTypeMapping mapping, Query query, CoordinateReferenceSystem targetCRS)
      throws IOException {
    mappedSource = mapping.getSource();

    // NC - joining query
    if (query instanceof JoiningQuery) {
      if (mappedSource instanceof JDBCFeatureSource) {
        mappedSource = new JoiningJDBCFeatureSource((JDBCFeatureSource) mappedSource);
      } else if (mappedSource instanceof JDBCFeatureStore) {
        mappedSource = new JoiningJDBCFeatureSource((JDBCFeatureStore) mappedSource);
      } else {
        throw new IllegalArgumentException(
            "Joining queries are only supported on JDBC data stores");
      }
    }
    String version =
        (String) this.mapping.getTargetFeature().getType().getUserData().get("targetVersion");
    // might be because top level feature has no geometry
    if (targetCRS == null && version != null) {
      // figure out the crs the data is in
      CoordinateReferenceSystem crs = null;
      try {
        crs = this.mappedSource.getSchema().getCoordinateReferenceSystem();
      } catch (UnsupportedOperationException e) {
        // do nothing as mappedSource is a WSFeatureSource
      }
      // gather declared CRS
      CoordinateReferenceSystem declaredCRS = this.getDeclaredCrs(crs, version);
      CoordinateReferenceSystem target;
      Object crsobject = this.mapping.getTargetFeature().getType().getUserData().get("targetCrs");
      if (crsobject instanceof CoordinateReferenceSystem) {
        target = (CoordinateReferenceSystem) crsobject;
      } else if (crsobject instanceof URI) {

        URI uri = (URI) crsobject;
        if (uri != null) {
          try {
            target = CRS.decode(uri.toString());
          } catch (Exception e) {
            String msg = "Unable to support srsName: " + uri;
            throw new UnsupportedOperationException(msg, e);
          }
        } else {
          target = declaredCRS;
        }
      } else {
        target = declaredCRS;
      }
      this.reprojection = target;

    } else {
      this.reprojection = targetCRS;
    }

    // clean up user data related to request
    mapping.getTargetFeature().getType().getUserData().put("targetVersion", null);
    mapping.getTargetFeature().getType().getUserData().put("targetCrs", null);

    // reproject target feature
    targetFeature = reprojectAttribute(mapping.getTargetFeature());

    // we need to disable the max number of features retrieved so we can
    // sort them manually just in case the data is denormalised
    query.setMaxFeatures(Query.DEFAULT_MAX);
    sourceFeatures = mappedSource.getFeatures(query);
    if (reprojection != null) {
      xpathAttributeBuilder.setCRS(reprojection);
      if (sourceFeatures.getSchema().getGeometryDescriptor() == null
          || this.isReprojectionCrsEqual(
              this.mappedSource.getSchema().getCoordinateReferenceSystem(), this.reprojection)) {
        // VT: No point trying to re-project without any geometry.
        query.setCoordinateSystemReproject(null);
      }
    }
    if (!(this instanceof XmlMappingFeatureIterator)) {
      this.sourceFeatureIterator = sourceFeatures.features();
    }

    // NC - joining nested atts
    for (AttributeMapping attMapping : selectedMapping) {

      if (attMapping instanceof JoiningNestedAttributeMapping) {
        ((JoiningNestedAttributeMapping) attMapping).open(this, query);
      }
    }
  }