protected Object getValues(
     boolean isMultiValued, Expression expression, Object sourceFeatureInput) {
   if (isMultiValued
       && sourceFeatureInput instanceof FeatureImpl
       && expression instanceof AttributeExpressionImpl) {
     // RA: Feature Chaining
     // complex features can have multiple nodes of the same attribute.. and if they are used
     // as input to an app-schema data access to be nested inside another feature type of a
     // different XML type, it has to be mapped like this:
     // <AttributeMapping>
     // <targetAttribute>
     // gsml:composition
     // </targetAttribute>
     // <sourceExpression>
     // <inputAttribute>mo:composition</inputAttribute>
     // <linkElement>gsml:CompositionPart</linkElement>
     // <linkField>gml:name</linkField>
     // </sourceExpression>
     // <isMultiple>true</isMultiple>
     // </AttributeMapping>
     // As there can be multiple nodes of mo:composition in this case, we need to retrieve
     // all of them
     AttributeExpressionImpl attribExpression = ((AttributeExpressionImpl) expression);
     String xpath = attribExpression.getPropertyName();
     ComplexAttribute sourceFeature = (ComplexAttribute) sourceFeatureInput;
     StepList xpathSteps = XPath.steps(sourceFeature.getDescriptor(), xpath, namespaces);
     return getProperties(sourceFeature, xpathSteps);
   }
   return expression.evaluate(sourceFeatureInput);
 }
  @Override
  public CloseableIterator<FileGroup> getFiles(Query query) {
    // normally the different type names are actually sharing the same files, but we cannot be
    // sure, a manually setup mosaic could indeed have multiple types with different files in
    // them...
    SimpleFeatureCollection fc = null;
    try {
      Query updatedQuery = query != null ? query : new Query();
      Filter filter = updatedQuery.getFilter();

      // TODO: Improve this check since it may contain multiple filters
      if (!"location".equalsIgnoreCase(locationAttributeName) && filter instanceof LikeFilterImpl) {
        // Rewrap the filter to update the file search
        LikeFilterImpl likeFilter = (LikeFilterImpl) filter;
        AttributeExpressionImpl impl = (AttributeExpressionImpl) likeFilter.getExpression();
        String attribute = impl.getPropertyName();
        String value = likeFilter.getLiteral();
        if ("location".equalsIgnoreCase(attribute)) {
          // The invoker provided a default "location" attribute.
          // make sure to remap it to the actual location attribute
          attribute = locationAttributeName;
          updatedQuery.setFilter(FF.like(FF.property(attribute), value));
        }
      }
      final List<SortBy> clauses = new ArrayList<SortBy>(1);
      clauses.add(
          new SortByImpl(
              FeatureUtilities.DEFAULT_FILTER_FACTORY.property(locationAttributeName),
              SortOrder.ASCENDING));
      final SortBy[] sb = clauses.toArray(new SortBy[] {});
      final boolean isSortBySupported =
          granuleCatalog.getQueryCapabilities(typeName).supportsSorting(sb);
      if (isSortBySupported) {
        updatedQuery.setSortBy(sb);
      }
      updatedQuery.setTypeName(typeName);

      // TODO: Make sure to add different iterator for stores
      // not supporting sortBy (which DB based stores don't support sorting?)

      // Get all the features matching the query
      fc = granuleCatalog.getGranules(updatedQuery);
      return new CloseableFileGroupIterator(fc.features());
    } catch (IOException ioe) {
      throw new RuntimeException(ioe);
    }
  }
  /** PropertyIsLike filter maps to a Contextual search criteria. */
  @Override
  public Object visit(PropertyIsLike filter, Object data) {
    logger.debug("ENTERING: PropertyIsLike filter");

    LikeFilterImpl likeFilter = (LikeFilterImpl) filter;

    AttributeExpressionImpl expression = (AttributeExpressionImpl) likeFilter.getExpression();
    String selectors = expression.getPropertyName();
    logger.debug("selectors = " + selectors);

    String searchPhrase = likeFilter.getLiteral();
    logger.debug("searchPhrase = [" + searchPhrase + "]");
    if (contextualSearch != null) {
      contextualSearch.setSearchPhrase(
          contextualSearch.getSearchPhrase() + " " + curNest.toString() + " " + searchPhrase);
    } else {
      contextualSearch = new ContextualSearch(selectors, searchPhrase, likeFilter.isMatchingCase());
    }

    logger.debug("EXITING: PropertyIsLike filter");

    return super.visit(filter, data);
  }