/**
  * Check the given property values against the required fields, generating missing field errors
  * where appropriate.
  *
  * @param mpvs the property values to be bound (can be modified)
  * @see #getRequiredFields
  * @see #getBindingErrorProcessor
  * @see BindingErrorProcessor#processMissingFieldError
  */
 protected void checkRequiredFields(MutablePropertyValues mpvs) {
   String[] requiredFields = getRequiredFields();
   if (!ObjectUtils.isEmpty(requiredFields)) {
     Map<String, PropertyValue> propertyValues = new HashMap<String, PropertyValue>();
     PropertyValue[] pvs = mpvs.getPropertyValues();
     for (PropertyValue pv : pvs) {
       String canonicalName = PropertyAccessorUtils.canonicalPropertyName(pv.getName());
       propertyValues.put(canonicalName, pv);
     }
     for (String field : requiredFields) {
       PropertyValue pv = propertyValues.get(field);
       boolean empty = (pv == null || pv.getValue() == null);
       if (!empty) {
         if (pv.getValue() instanceof String) {
           empty = !StringUtils.hasText((String) pv.getValue());
         } else if (pv.getValue() instanceof String[]) {
           String[] values = (String[]) pv.getValue();
           empty = (values.length == 0 || !StringUtils.hasText(values[0]));
         }
       }
       if (empty) {
         // Use bind error processor to create FieldError.
         getBindingErrorProcessor().processMissingFieldError(field, getInternalBindingResult());
         // Remove property from property values to bind:
         // It has already caused a field error with a rejected value.
         if (pv != null) {
           mpvs.removePropertyValue(pv);
           propertyValues.remove(field);
         }
       }
     }
   }
 }
 private void filterNestedParameterMaps(MutablePropertyValues mpvs) {
   for (PropertyValue pv : mpvs.getPropertyValues()) {
     final Object value = pv.getValue();
     if (isNotCandidateForBinding(value)) {
       mpvs.removePropertyValue(pv);
     }
   }
 }
 /**
  * Checks for structured properties. Structured properties are properties with a name containg a
  * "_"
  *
  * @param propertyValues
  */
 @SuppressWarnings("unchecked")
 private void checkStructuredProperties(MutablePropertyValues propertyValues) {
   PropertyValue[] pvs = propertyValues.getPropertyValues();
   for (PropertyValue propertyValue : pvs) {
     if (!isStructured(propertyValue)) {
       continue;
     }
     String propertyName = getNameOf(propertyValue);
     Class<?> type = bean.getPropertyType(propertyName);
     if (type != null) {
       PropertyEditor editor = findCustomEditor(type, propertyName);
       if (null != editor && StructuredPropertyEditor.class.isAssignableFrom(editor.getClass())) {
         StructuredPropertyEditor structuredEditor = (StructuredPropertyEditor) editor;
         List fields = new ArrayList();
         fields.addAll(structuredEditor.getRequiredFields());
         fields.addAll(structuredEditor.getOptionalFields());
         Map<String, String> fieldValues = new HashMap<String, String>();
         try {
           for (Object fld : fields) {
             String field = (String) fld;
             PropertyValue partialStructValue =
                 propertyValues.getPropertyValue(
                     propertyName + STRUCTURED_PROPERTY_SEPERATOR + field);
             if (partialStructValue == null
                 && structuredEditor.getRequiredFields().contains(field)) {
               throw new MissingPropertyException(
                   "Required structured property is missing [" + field + "]");
             } else if (partialStructValue == null) {
               continue;
             }
             fieldValues.put(field, getStringValue(partialStructValue));
           }
           try {
             Object value = structuredEditor.assemble(type, fieldValues);
             for (Object fld : fields) {
               String field = (String) fld;
               PropertyValue partialStructValue =
                   propertyValues.getPropertyValue(
                       propertyName + STRUCTURED_PROPERTY_SEPERATOR + field);
               if (null != partialStructValue) {
                 partialStructValue.setConvertedValue(getStringValue(partialStructValue));
               }
             }
             propertyValues.addPropertyValue(new PropertyValue(propertyName, value));
           } catch (IllegalArgumentException iae) {
             LOG.warn(
                 "Unable to parse structured date from request for date [" + propertyName + "]",
                 iae);
           }
         } catch (InvalidPropertyException ipe) {
           // ignore
         }
       }
     }
   }
 }
 /**
  * Check the given property values against the allowed fields, removing values for fields that are
  * not allowed.
  *
  * @param mpvs the property values to be bound (can be modified)
  * @see #getAllowedFields
  * @see #isAllowed(String)
  */
 protected void checkAllowedFields(MutablePropertyValues mpvs) {
   PropertyValue[] pvs = mpvs.getPropertyValues();
   for (PropertyValue pv : pvs) {
     String field = PropertyAccessorUtils.canonicalPropertyName(pv.getName());
     if (!isAllowed(field)) {
       mpvs.removePropertyValue(pv);
       getBindingResult().recordSuppressedField(field);
       if (logger.isDebugEnabled()) {
         logger.debug(
             "Field ["
                 + field
                 + "] has been removed from PropertyValues "
                 + "and will not be bound, because it has not been found in the list of allowed fields");
       }
     }
   }
 }
  @SuppressWarnings("unchecked")
  private void filterBlankValuesWhenTargetIsNullable(MutablePropertyValues mpvs) {
    Object target = getTarget();
    Map constrainedProperties = resolveConstrainedProperties(target, domainClass);
    if (constrainedProperties == null) {
      return;
    }

    PropertyValue[] valueArray = mpvs.getPropertyValues();
    for (PropertyValue propertyValue : valueArray) {
      if (BLANK.equals(propertyValue.getValue())) {
        ConstrainedProperty cp =
            getConstrainedPropertyForPropertyValue(constrainedProperties, propertyValue);
        if (shouldNullifyBlankString(propertyValue, cp)) {
          propertyValue.setConvertedValue(null);
        }
      }
    }
  }
  /**
   * Auto-creates the a type if it is null and is possible to auto-create.
   *
   * @param mpvs A MutablePropertyValues instance
   */
  protected void autoCreateIfPossible(MutablePropertyValues mpvs) {
    PropertyValue[] pvs = mpvs.getPropertyValues();
    for (PropertyValue pv : pvs) {
      String propertyName = pv.getName();
      if (propertyName.indexOf(PATH_SEPARATOR) > -1) {
        String[] propertyNames = propertyName.split("\\.");
        BeanWrapper currentBean = bean;

        for (String name : propertyNames) {
          Object created = autoCreatePropertyIfPossible(currentBean, name, pv.getValue());
          if (created != null) {
            currentBean = new BeanWrapperImpl(created);
          } else {
            break;
          }
        }
      } else {
        autoCreatePropertyIfPossible(bean, propertyName, pv.getValue());
      }
    }
  }
  /**
   * This overrides the method from WebDataBinder to allow for nested checkbox handling, so property
   * paths such as a._b will result in the boolean b on object a getting set to false.
   */
  @Override
  protected void checkFieldMarkers(MutablePropertyValues mpvs) {
    if (getFieldMarkerPrefix() == null) {
      return;
    }

    String fieldMarkerPrefix = getFieldMarkerPrefix();
    PropertyValue[] pvArray = mpvs.getPropertyValues();
    for (PropertyValue pv : pvArray) {
      // start of variation from superclass method
      if (propertyStartsWithFieldMarkerPrefix(pv, fieldMarkerPrefix)) {
        String field = stripFieldMarkerPrefix(pv.getName(), fieldMarkerPrefix);
        // end of variation from superclass method
        if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) {
          Class<?> fieldType = getPropertyAccessor().getPropertyType(field);
          mpvs.add(field, getEmptyValue(field, fieldType));
        }
        mpvs.removePropertyValue(pv);
      }
    }
  }
  /**
   * Interrogates the specified properties looking for properites that represent associations to
   * other classes (e.g., 'author.id'). If such a property is found, this method attempts to load
   * the specified instance of the association (by ID) and set it on the target object.
   *
   * @param mpvs the <code>MutablePropertyValues</code> object holding the parameters from the
   *     request
   */
  protected void bindAssociations(MutablePropertyValues mpvs) {
    for (PropertyValue pv : mpvs.getPropertyValues()) {
      String propertyName = pv.getName();
      String propertyNameToCheck = propertyName;
      final int i = propertyName.indexOf('.');
      if (i > -1) {
        propertyNameToCheck = propertyName.substring(0, i);
      }

      if (!isAllowed(propertyNameToCheck)) continue;

      if (propertyName.endsWith(IDENTIFIER_SUFFIX)) {
        propertyName = propertyName.substring(0, propertyName.length() - 3);
        if (!isAllowed(propertyName)) continue;
        if (isReadableAndPersistent(propertyName) && bean.isWritableProperty(propertyName)) {
          if (NULL_ASSOCIATION.equals(pv.getValue())) {
            bean.setPropertyValue(propertyName, null);
            mpvs.removePropertyValue(pv);
          } else {
            Class<?> type = getPropertyTypeForPath(propertyName);
            Object persisted = getPersistentInstance(type, pv.getValue());
            if (persisted != null) {
              bean.setPropertyValue(propertyName, persisted);
            }
          }
        }
      } else {
        if (isReadableAndPersistent(propertyName)) {
          Class<?> type = getPropertyTypeForPath(propertyName);
          if (Collection.class.isAssignableFrom(type)) {
            bindCollectionAssociation(mpvs, pv);
          }
        }
      }
    }
  }