/** * 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); } } } } }