protected boolean skipTopElement(
     Name topElement, AttributeMapping attMapping, AttributeType type) {
   // don't skip if there's OCQL
   return XPath.equals(topElement, attMapping.getTargetXPath())
       && (attMapping.getSourceExpression() == null
           || Expression.NIL.equals(attMapping.getSourceExpression()));
 }
    private UserGroupRestRep update() {
      UserGroupUpdateParam param = new UserGroupUpdateParam();
      UserGroupRestRep userGroupRestRep = UserGroupUtils.getUserGroup(this.id);

      param.setLabel(userGroupRestRep.getName());
      param.setDomain(this.domain);

      Set<UserAttributeParam> oldAttributes = userGroupRestRep.getAttributes();
      Set<UserAttributeParam> newAttributes = new HashSet<UserAttributeParam>();
      for (AttributeMapping mapping : this.attributes) {
        if (mapping != null) {
          newAttributes.add(mapping.createUserAttributeParam());
        }
      }

      param.getAddAttributes().addAll(newAttributes);
      param.getAddAttributes().removeAll(oldAttributes);

      for (UserAttributeParam oldAttribute : oldAttributes) {
        param.getRemoveAttributes().add(oldAttribute.getKey());
      }

      for (UserAttributeParam newAttribute : newAttributes) {
        param.getRemoveAttributes().remove(newAttribute.getKey());
      }

      return UserGroupUtils.update(this.id, param);
    }
  // properties can only be set by constructor, before initialising source features
  // (for joining nested mappings)
  private void setPropertyNames(Collection<PropertyName> propertyNames) {
    selectedProperties = new HashMap<AttributeMapping, List<PropertyName>>();

    if (propertyNames == null) {
      selectedMapping = mapping.getAttributeMappings();
    } else {
      final AttributeDescriptor targetDescriptor = mapping.getTargetFeature();
      selectedMapping = new ArrayList<AttributeMapping>();

      for (AttributeMapping attMapping : mapping.getAttributeMappings()) {
        final StepList targetSteps = attMapping.getTargetXPath();
        boolean alreadyAdded = false;

        if (includeMandatory) {
          PropertyName targetProp = namespaceAwareFilterFactory.property(targetSteps.toString());
          Object descr = targetProp.evaluate(targetDescriptor.getType());
          if (descr instanceof PropertyDescriptor) {
            if (((PropertyDescriptor) descr).getMinOccurs() >= 1) {
              selectedMapping.add(attMapping);
              selectedProperties.put(attMapping, new ArrayList<PropertyName>());
              alreadyAdded = true;
            }
          }
        }

        for (PropertyName requestedProperty : propertyNames) {
          StepList requestedPropertySteps;
          if (requestedProperty.getNamespaceContext() == null) {
            requestedPropertySteps =
                XPath.steps(targetDescriptor, requestedProperty.getPropertyName(), namespaces);
          } else {
            requestedPropertySteps =
                XPath.steps(
                    targetDescriptor,
                    requestedProperty.getPropertyName(),
                    requestedProperty.getNamespaceContext());
          }
          if (requestedPropertySteps == null
              ? AppSchemaDataAccess.matchProperty(requestedProperty.getPropertyName(), targetSteps)
              : AppSchemaDataAccess.matchProperty(requestedPropertySteps, targetSteps)) {
            if (!alreadyAdded) {
              selectedMapping.add(attMapping);
              selectedProperties.put(attMapping, new ArrayList<PropertyName>());
              alreadyAdded = true;
            }
            if (requestedPropertySteps != null
                && requestedPropertySteps.size() > targetSteps.size()) {
              List<PropertyName> pnList = selectedProperties.get(attMapping);
              StepList subProperty =
                  requestedPropertySteps.subList(targetSteps.size(), requestedPropertySteps.size());
              pnList.add(
                  filterFac.property(
                      subProperty.toString(), requestedProperty.getNamespaceContext()));
            }
          }
        }
      }
    }
  }
    private UserGroupRestRep create() {
      UserGroupCreateParam param = new UserGroupCreateParam();
      param.setLabel(this.name);
      param.setDomain(this.domain);
      for (AttributeMapping mapping : this.attributes) {
        if (mapping != null) {
          param.getAttributes().add(mapping.createUserAttributeParam());
        }
      }

      return UserGroupUtils.create(param);
    }
 /**
  * Special handling for polymorphic mapping. Works out the polymorphic type name by evaluating the
  * function on the feature, then set the relevant sub-type values.
  *
  * @param target The target feature to be encoded
  * @param id The target feature id
  * @param nestedMapping The mapping that is polymorphic
  * @param source The source simple feature
  * @param xpath The xpath of polymorphic type
  * @param clientPropsMappings Client properties
  * @throws IOException
  */
 private Attribute setPolymorphicValues(
     Name mappingName,
     Attribute target,
     String id,
     NestedAttributeMapping nestedMapping,
     Object source,
     StepList xpath,
     Map<Name, Expression> clientPropsMappings)
     throws IOException {
   // process sub-type mapping
   DataAccess<FeatureType, Feature> da = DataAccessRegistry.getDataAccess((Name) mappingName);
   if (da instanceof AppSchemaDataAccess) {
     // why wouldn't it be? check just to be safe
     FeatureTypeMapping fTypeMapping =
         ((AppSchemaDataAccess) da).getMappingByName((Name) mappingName);
     List<AttributeMapping> polymorphicMappings = fTypeMapping.getAttributeMappings();
     AttributeDescriptor attDescriptor = fTypeMapping.getTargetFeature();
     AttributeType type = attDescriptor.getType();
     Name polymorphicTypeName = attDescriptor.getName();
     StepList prefixedXpath = xpath.clone();
     prefixedXpath.add(
         new Step(
             new QName(
                 polymorphicTypeName.getNamespaceURI(),
                 polymorphicTypeName.getLocalPart(),
                 this.namespaces.getPrefix(polymorphicTypeName.getNamespaceURI())),
             1));
     if (!fTypeMapping.getFeatureIdExpression().equals(Expression.NIL)) {
       id = fTypeMapping.getFeatureIdExpression().evaluate(source, String.class);
     }
     Attribute instance =
         xpathAttributeBuilder.set(
             target, prefixedXpath, null, id, type, false, attDescriptor, null);
     setClientProperties(instance, source, clientPropsMappings);
     for (AttributeMapping mapping : polymorphicMappings) {
       if (skipTopElement(polymorphicTypeName, mapping, type)) {
         // if the top level mapping for the Feature itself, the attribute instance
         // has already been created.. just need to set the client properties
         setClientProperties(instance, source, mapping.getClientProperties());
         continue;
       }
       setAttributeValue(
           instance, null, source, mapping, null, null, selectedProperties.get(mapping));
     }
     return instance;
   }
   return null;
 }
    public void readFrom(UserGroupRestRep userGroupRep) {
      this.id = stringId(userGroupRep);
      this.name = userGroupRep.getName();
      this.domain = userGroupRep.getDomain();

      if (!CollectionUtils.isEmpty(userGroupRep.getAttributes())) {
        for (UserAttributeParam userAttributeMapping : userGroupRep.getAttributes()) {
          if (userAttributeMapping != null) {
            AttributeMapping mapping = new AttributeMapping();
            mapping.key = userAttributeMapping.getKey();
            mapping.values = StringUtils.join(userAttributeMapping.getValues(), "\n");
            this.attributes.add(mapping);
          }
        }
      }
    }
  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));
        }
      }
    }
  }
  /**
   * Create an environment for AppEngine API calls in the context of a request.
   *
   * @param envMap a map containing environment variables (from System.getenv()).
   * @param cache the VM meta-data cache used to retrieve VM attributes.
   * @param request the HTTP request to get header values from.
   * @param server the host:port where the VMs API proxy is bound to.
   * @param wallTimer optional wall clock timer for the current request (required for deadline).
   * @param millisUntilSoftDeadline optional soft deadline in milliseconds relative to 'wallTimer'.
   * @return the created Environment object which can be registered with the ApiProxy.
   */
  public static VmApiProxyEnvironment createFromHeaders(
      Map<String, String> envMap,
      VmMetadataCache cache,
      HttpRequest request,
      String server,
      Timer wallTimer,
      Long millisUntilSoftDeadline,
      VmApiProxyEnvironment defaultEnvironment) {
    final String longAppId = getEnvOrMetadata(envMap, cache, LONG_APP_ID_KEY, PROJECT_ATTRIBUTE);
    final String partition = getEnvOrMetadata(envMap, cache, PARTITION_KEY, PARTITION_ATTRIBUTE);
    final String module = getEnvOrMetadata(envMap, cache, MODULE_NAME_KEY, BACKEND_ATTRIBUTE);
    final String majorVersion = defaultEnvironment.getMajorVersion();
    final String minorVersion = defaultEnvironment.getMinorVersion();
    final String appengineHostname = defaultEnvironment.getAppengineHostname();
    final boolean useMvmAgent = defaultEnvironment.getUseMvmAgent();
    final String instance = getEnvOrMetadata(envMap, cache, INSTANCE_KEY, INSTANCE_ATTRIBUTE);
    final String affinity = getEnvOrMetadata(envMap, cache, AFFINITY_ENV_KEY, AFFINITY_ATTRIBUTE);
    final String ticket = request.getHeader(TICKET_HEADER);
    final String email = request.getHeader(EMAIL_HEADER);
    boolean admin = false;
    String value = request.getHeader(IS_ADMIN_HEADER);
    if (value != null && !value.trim().isEmpty()) {
      try {
        admin = Integer.parseInt(value.trim()) != 0;
      } catch (NumberFormatException e) {
        throw new IllegalArgumentException(e.getMessage(), e);
      }
    }
    final String authDomain = request.getHeader(AUTH_DOMAIN_HEADER);
    boolean trustedApp = request.getHeader(IS_TRUSTED_IP_HEADER) != null;

    Map<String, Object> attributes = new HashMap<>();
    // Fill in the attributes from the AttributeMapping.
    for (AttributeMapping mapping : AttributeMapping.values()) {
      if (mapping.trustedAppOnly && !trustedApp) {
        // Do not fill in any trusted app attributes unless the app is trusted.
        continue;
      }
      String headerValue = request.getHeader(mapping.headerKey);
      if (headerValue != null) {
        attributes.put(mapping.attributeKey, headerValue);
      } else if (mapping.defaultValue != null) {
        attributes.put(mapping.attributeKey, mapping.defaultValue);
      } // else: The attribute is expected to be missing if the header is not set.
    }

    // Fill in the special attributes that do not fit the simple mapping model.
    boolean federatedId = request.getHeader(AttributeMapping.FEDERATED_IDENTITY.headerKey) != null;
    attributes.put(IS_FEDERATED_USER_KEY, federatedId);

    attributes.put(BACKEND_ID_KEY, module);
    attributes.put(INSTANCE_ID_KEY, instance);
    attributes.put(AFFINITY_KEY, affinity);

    if (trustedApp) {
      // The trusted IP attribute is a boolean.
      boolean trustedIp = "1".equals(request.getHeader(IS_TRUSTED_IP_HEADER));
      attributes.put(IS_TRUSTED_IP_KEY, trustedIp);
    }

    VmApiProxyEnvironment requestEnvironment =
        new VmApiProxyEnvironment(
            server,
            ticket,
            longAppId,
            partition,
            module,
            majorVersion,
            minorVersion,
            instance,
            appengineHostname,
            email,
            admin,
            authDomain,
            useMvmAgent,
            wallTimer,
            millisUntilSoftDeadline,
            attributes);
    // Add the thread factories required by the threading API.
    attributes.put(REQUEST_THREAD_FACTORY_ATTR, new VmRequestThreadFactory(requestEnvironment));
    // Since we register VmEnvironmentFactory with ApiProxy in VmRuntimeWebAppContext,
    // we can use the default thread factory here and don't require any special logic.
    attributes.put(BACKGROUND_THREAD_FACTORY_ATTR, Executors.defaultThreadFactory());

    return requestEnvironment;
  }
  /**
   * Creates an environment for AppEngine API calls outside the context of a request.
   *
   * @param envMap a map containing environment variables (from System.getenv()).
   * @param cache the VM meta-data cache used to retrieve VM attributes.
   * @param server the host:port where the VMs API proxy is bound to.
   * @param wallTimer optional wall clock timer for the current request (required for deadline).
   * @param millisUntilSoftDeadline optional soft deadline in milliseconds relative to 'wallTimer'.
   * @param appDir the canonical folder of the application.
   * @return the created Environment object which can be registered with the ApiProxy.
   */
  public static VmApiProxyEnvironment createDefaultContext(
      Map<String, String> envMap,
      VmMetadataCache cache,
      String server,
      Timer wallTimer,
      Long millisUntilSoftDeadline,
      String appDir) {
    final String longAppId = getEnvOrMetadata(envMap, cache, LONG_APP_ID_KEY, PROJECT_ATTRIBUTE);
    final String partition = getEnvOrMetadata(envMap, cache, PARTITION_KEY, PARTITION_ATTRIBUTE);
    final String module = getEnvOrMetadata(envMap, cache, MODULE_NAME_KEY, BACKEND_ATTRIBUTE);
    final String majorVersion = getEnvOrMetadata(envMap, cache, VERSION_KEY, VERSION_ATTRIBUTE);
    String minorVersion = envMap.get(MINOR_VERSION_KEY);
    if (minorVersion == null) {
      minorVersion = VmRuntimeUtils.getMinorVersionFromPath(majorVersion, appDir);
    }
    final String instance = getEnvOrMetadata(envMap, cache, INSTANCE_KEY, INSTANCE_ATTRIBUTE);
    final String affinity = getEnvOrMetadata(envMap, cache, AFFINITY_ENV_KEY, AFFINITY_ATTRIBUTE);
    final String appengineHostname =
        getEnvOrMetadata(envMap, cache, APPENGINE_HOSTNAME_KEY, APPENGINE_HOSTNAME_ATTRIBUTE);
    final String ticket = null;
    final String email = null;
    final boolean admin = false;
    final String authDomain = null;
    final boolean useMvmAgent =
        Boolean.parseBoolean(
            getEnvOrMetadata(envMap, cache, USE_MVM_AGENT_KEY, USE_MVM_AGENT_ATTRIBUTE));

    Map<String, Object> attributes = new HashMap<>();
    // Fill in default attributes values.
    for (AttributeMapping mapping : AttributeMapping.values()) {
      if (mapping.trustedAppOnly) {
        continue;
      }
      if (mapping.defaultValue == null) {
        continue;
      }
      attributes.put(mapping.attributeKey, mapping.defaultValue);
    }
    attributes.put(IS_FEDERATED_USER_KEY, Boolean.FALSE);
    attributes.put(BACKEND_ID_KEY, module);
    attributes.put(INSTANCE_ID_KEY, instance);
    attributes.put(AFFINITY_KEY, affinity);
    VmApiProxyEnvironment defaultEnvironment =
        new VmApiProxyEnvironment(
            server,
            ticket,
            longAppId,
            partition,
            module,
            majorVersion,
            minorVersion,
            instance,
            appengineHostname,
            email,
            admin,
            authDomain,
            useMvmAgent,
            wallTimer,
            millisUntilSoftDeadline,
            attributes);
    // Add the thread factories required by the threading API.
    attributes.put(REQUEST_THREAD_FACTORY_ATTR, new VmRequestThreadFactory(null));
    // Since we register VmEnvironmentFactory with ApiProxy in VmRuntimeWebAppContext,
    // we can use the default thread factory here and don't require any special logic.
    attributes.put(BACKGROUND_THREAD_FACTORY_ATTR, Executors.defaultThreadFactory());
    return defaultEnvironment;
  }
  protected Feature computeNext() throws IOException {

    String id = getNextFeatureId();
    List<Feature> sources = getSources(id);

    final Name targetNodeName = targetFeature.getName();

    AttributeBuilder builder = new AttributeBuilder(attf);
    builder.setDescriptor(targetFeature);
    Feature target = (Feature) builder.build(id);

    for (AttributeMapping attMapping : selectedMapping) {
      try {
        if (skipTopElement(targetNodeName, attMapping, targetFeature.getType())) {
          // ignore the top level mapping for the Feature itself
          // as it was already set
          continue;
        }
        if (attMapping.isList()) {
          Attribute instance =
              setAttributeValue(
                  target,
                  null,
                  sources.get(0),
                  attMapping,
                  null,
                  null,
                  selectedProperties.get(attMapping));
          if (sources.size() > 1 && instance != null) {
            List<Object> values = new ArrayList<Object>();
            Expression sourceExpr = attMapping.getSourceExpression();
            for (Feature source : sources) {
              values.add(getValue(sourceExpr, source));
            }
            String valueString = StringUtils.join(values.iterator(), " ");
            StepList fullPath = attMapping.getTargetXPath();
            StepList leafPath = fullPath.subList(fullPath.size() - 1, fullPath.size());
            if (instance instanceof ComplexAttributeImpl) {
              // xpath builder will work out the leaf attribute to set values on
              xpathAttributeBuilder.set(
                  instance, leafPath, valueString, null, null, false, sourceExpr);
            } else {
              // simple attributes
              instance.setValue(valueString);
            }
          }
        } else if (attMapping.isMultiValued()) {
          // extract the values from multiple source features of the same id
          // and set them to one built feature
          for (Feature source : sources) {
            setAttributeValue(
                target, null, source, attMapping, null, null, selectedProperties.get(attMapping));
          }
        } else {
          String indexString = attMapping.getSourceIndex();
          // if not specified, get the first row by default
          int index = 0;
          if (indexString != null) {
            if (ComplexFeatureConstants.LAST_INDEX.equals(indexString)) {
              index = sources.size() - 1;
            } else {
              index = Integer.parseInt(indexString);
            }
          }
          setAttributeValue(
              target,
              null,
              sources.get(index),
              attMapping,
              null,
              null,
              selectedProperties.get(attMapping));
          // When a feature is not multi-valued but still has multiple rows with the same ID in
          // a denormalised table, by default app-schema only takes the first row and ignores
          // the rest (see above). The following line is to make sure that the cursors in the
          // 'joining nested mappings'skip any extra rows that were linked to those rows that are
          // being ignored.
          // Otherwise the cursor will stay there in the wrong spot and none of the following
          // feature chaining
          // will work. That can really only occur if the foreign key is not unique for the ID of
          // the parent
          // feature (otherwise all of those rows would be already passed when creating the feature
          // based on
          // the first row). This never really occurs in practice I have noticed, but it is a
          // theoretic
          // possibility, as there is no requirement for the foreign key to be unique per id.
          skipNestedMapping(attMapping, sources.subList(1, sources.size()));
        }
      } catch (Exception e) {
        throw new RuntimeException(
            "Error applying mapping with targetAttribute " + attMapping.getTargetXPath(), e);
      }
    }
    cleanEmptyElements(target);

    return target;
  }
  /**
   * Sets the values of grouping attributes.
   *
   * @param target
   * @param source
   * @param attMapping
   * @param values
   * @return Feature. Target feature sets with simple attributes
   */
  protected Attribute setAttributeValue(
      Attribute target,
      String id,
      final Object source,
      final AttributeMapping attMapping,
      Object values,
      StepList inputXpath,
      List<PropertyName> selectedProperties)
      throws IOException {

    final Expression sourceExpression = attMapping.getSourceExpression();
    final AttributeType targetNodeType = attMapping.getTargetNodeInstance();
    StepList xpath = inputXpath == null ? attMapping.getTargetXPath().clone() : inputXpath;

    Map<Name, Expression> clientPropsMappings = attMapping.getClientProperties();
    boolean isNestedFeature = attMapping.isNestedAttribute();

    if (id == null && Expression.NIL != attMapping.getIdentifierExpression()) {
      id = extractIdForAttribute(attMapping.getIdentifierExpression(), source);
    }
    if (attMapping.isNestedAttribute()) {
      NestedAttributeMapping nestedMapping = ((NestedAttributeMapping) attMapping);
      Object mappingName = nestedMapping.getNestedFeatureType(source);
      if (mappingName != null) {
        if (nestedMapping.isSameSource() && mappingName instanceof Name) {
          // data type polymorphism mapping
          return setPolymorphicValues(
              (Name) mappingName, target, id, nestedMapping, source, xpath, clientPropsMappings);
        } else if (mappingName instanceof String) {
          // referential polymorphism mapping
          return setPolymorphicReference(
              (String) mappingName, clientPropsMappings, target, xpath, targetNodeType);
        }
      } else {
        // polymorphism could result in null, to skip the attribute
        return null;
      }
    }
    if (values == null && source != null) {
      values = getValues(attMapping.isMultiValued(), sourceExpression, source);
    }
    boolean isHRefLink = isByReference(clientPropsMappings, isNestedFeature);
    if (isNestedFeature) {
      if (values == null) {
        // polymorphism use case, if the value doesn't match anything, don't encode
        return null;
      }
      // get built feature based on link value
      if (values instanceof Collection) {
        ArrayList<Attribute> nestedFeatures =
            new ArrayList<Attribute>(((Collection) values).size());
        for (Object val : (Collection) values) {
          if (val instanceof Attribute) {
            val = ((Attribute) val).getValue();
            if (val instanceof Collection) {
              val = ((Collection) val).iterator().next();
            }
            while (val instanceof Attribute) {
              val = ((Attribute) val).getValue();
            }
          }
          if (isHRefLink) {
            // get the input features to avoid infinite loop in case the nested
            // feature type also have a reference back to this type
            // eg. gsml:GeologicUnit/gsml:occurence/gsml:MappedFeature
            // and gsml:MappedFeature/gsml:specification/gsml:GeologicUnit
            nestedFeatures.addAll(
                ((NestedAttributeMapping) attMapping)
                    .getInputFeatures(
                        this,
                        val,
                        getIdValues(source),
                        source,
                        reprojection,
                        selectedProperties,
                        includeMandatory));
          } else {
            nestedFeatures.addAll(
                ((NestedAttributeMapping) attMapping)
                    .getFeatures(
                        this,
                        val,
                        getIdValues(source),
                        reprojection,
                        source,
                        selectedProperties,
                        includeMandatory));
          }
        }
        values = nestedFeatures;
      } else if (isHRefLink) {
        // get the input features to avoid infinite loop in case the nested
        // feature type also have a reference back to this type
        // eg. gsml:GeologicUnit/gsml:occurence/gsml:MappedFeature
        // and gsml:MappedFeature/gsml:specification/gsml:GeologicUnit
        values =
            ((NestedAttributeMapping) attMapping)
                .getInputFeatures(
                    this,
                    values,
                    getIdValues(source),
                    source,
                    reprojection,
                    selectedProperties,
                    includeMandatory);
      } else {
        values =
            ((NestedAttributeMapping) attMapping)
                .getFeatures(
                    this,
                    values,
                    getIdValues(source),
                    reprojection,
                    source,
                    selectedProperties,
                    includeMandatory);
      }
      if (isHRefLink) {
        // only need to set the href link value, not the nested feature properties
        setXlinkReference(target, clientPropsMappings, values, xpath, targetNodeType);
        return null;
      }
    }
    Attribute instance = null;
    if (values instanceof Collection) {
      // nested feature type could have multiple instances as the whole purpose
      // of feature chaining is to cater for multi-valued properties
      for (Object singleVal : (Collection) values) {
        ArrayList valueList = new ArrayList();
        // copy client properties from input features if they're complex features
        // wrapped in app-schema data access
        if (singleVal instanceof Attribute) {
          // copy client properties from input features if they're complex features
          // wrapped in app-schema data access
          Map<Name, Expression> valueProperties = getClientProperties((Attribute) singleVal);
          if (!valueProperties.isEmpty()) {
            clientPropsMappings.putAll(valueProperties);
          }
        }
        if (!isNestedFeature) {
          if (singleVal instanceof Attribute) {
            singleVal = ((Attribute) singleVal).getValue();
          }
          if (singleVal instanceof Collection) {
            valueList.addAll((Collection) singleVal);
          } else {
            valueList.add(singleVal);
          }
        } else {
          valueList.add(singleVal);
        }
        instance =
            xpathAttributeBuilder.set(
                target, xpath, valueList, id, targetNodeType, false, sourceExpression);
        setClientProperties(instance, source, clientPropsMappings);
      }
    } else {
      if (values instanceof Attribute) {
        // copy client properties from input features if they're complex features
        // wrapped in app-schema data access
        Map<Name, Expression> newClientProps = getClientProperties((Attribute) values);
        if (!newClientProps.isEmpty()) {
          newClientProps.putAll(clientPropsMappings);
          clientPropsMappings = newClientProps;
        }
        values = ((Attribute) values).getValue();
      }
      instance =
          xpathAttributeBuilder.set(
              target, xpath, values, id, targetNodeType, false, sourceExpression);
      setClientProperties(instance, source, clientPropsMappings);
    }
    if (instance != null && attMapping.encodeIfEmpty()) {
      instance.getDescriptor().getUserData().put("encodeIfEmpty", attMapping.encodeIfEmpty());
    }
    return instance;
  }