org.broadleafcommerce.openadmin.web.form.entity.EntityFormValidator.java Source code

Java tutorial

Introduction

Here is the source code for org.broadleafcommerce.openadmin.web.form.entity.EntityFormValidator.java

Source

/*
 * #%L
 * BroadleafCommerce Open Admin Platform
 * %%
 * Copyright (C) 2009 - 2013 Broadleaf Commerce
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package org.broadleafcommerce.openadmin.web.form.entity;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.broadleafcommerce.openadmin.dto.Entity;
import org.broadleafcommerce.openadmin.server.service.AdminEntityService;
import org.broadleafcommerce.openadmin.server.service.JSCompatibilityHelper;
import org.springframework.stereotype.Component;
import org.springframework.validation.AbstractBindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;

import java.util.List;
import java.util.Map;

/**
 * Validator used at the controller level to ensure that an Entity has passed validation from the PersistenceModule or
 * CustomPersistenceHandler. This should be used as a final validation step after attempting the save
 * 
 * @author Phillip Verheyden
 */
@Component("blEntityFormValidator")
public class EntityFormValidator {

    /**
     * Validates the DTO against the map of property errors. Note that this method does not support global validation errors
     * from {@link Entity#getGlobalValidationErrors()} as they might not make sense.
     * 
     * @param form
     * @param propertyErrors
     * @param errors
     * @return
     */
    public boolean validate(EntityForm form, Map<String, List<String>> propertyErrors, Errors errors) {
        return validate(form, propertyErrors, null, errors);
    }

    /**
     * Validates the form DTO against the passed in map of propertyErrors along with global validation errors.
     * 
     * @see #validate(EntityForm, Entity, Errors)
     * @param form
     * @param propertyErrors
     * @param globalErrors
     * @param errors
     * @return
     */
    public boolean validate(EntityForm form, Map<String, List<String>> propertyErrors, List<String> globalErrors,
            Errors errors) {
        if (MapUtils.isEmpty(propertyErrors) && CollectionUtils.isEmpty(globalErrors)) {
            return true;
        }

        if (MapUtils.isNotEmpty(propertyErrors)) {
            for (Map.Entry<String, List<String>> pe : propertyErrors.entrySet()) {
                for (String errorMessage : pe.getValue()) {
                    String unserializedFieldName = pe.getKey();
                    String serializedFieldName = JSCompatibilityHelper.encode(unserializedFieldName);

                    /**
                     * Rather than just use errors.rejectValue directly, we need to instantiate the FieldError object ourself
                     * and add it to the binding result. This is so that we can resolve the actual rejected value ourselves
                     * rather than rely on Spring to do it for us. If we rely on Spring, Spring will attempt to resolve something
                     * like fields['defaultSku__name'] immediately (at the time of invoking errors.rejectValue). At that point,
                     * the field names within the EntityForm are still in their unserialized state, and thus Spring would only
                     * find fields['defaultSku.name'] and there would be an empty string for the rejected value. Then on the
                     * frontend, Thymeleaf's th:field processor relies on Spring's BindingResult to provide the value that
                     * was actually rejected so you can get blank form fields.
                     * 
                     * With this implementation, we avoid all of those additional problems because we are referencing the
                     * field that is being rejected along with providing our own method for getting the rejected value
                     */
                    Field field = null;
                    if (StringUtils.contains(unserializedFieldName, DynamicEntityFormInfo.FIELD_SEPARATOR)) {
                        String[] fieldInfo = unserializedFieldName
                                .split("\\" + DynamicEntityFormInfo.FIELD_SEPARATOR);
                        field = form.getDynamicForm(fieldInfo[0]).getFields().get(fieldInfo[1]);
                    } else if (form.getFields().get(unserializedFieldName) != null) {
                        field = form.getFields().get(unserializedFieldName);
                    }

                    //If the submitted field was a radio button but has validation associated with it, that radio field
                    //will have never been submitted in the POST and thus will not have ever been attached to the EntityForm.
                    //We still want to notate the fact that there was a validation failure on that field
                    String value = (field != null) ? field.getValue() : null;

                    String[] errorCodes = ((AbstractBindingResult) errors).resolveMessageCodes(errorMessage,
                            serializedFieldName);
                    FieldError fieldError = new FieldError("entityForm",
                            String.format("fields[%s].value", serializedFieldName), value, false, errorCodes, null,
                            errorMessage);
                    ((AbstractBindingResult) errors).addError(fieldError);
                }
            }
        }

        if (CollectionUtils.isNotEmpty(globalErrors)) {
            for (String errorMessage : globalErrors) {
                errors.reject(errorMessage, errorMessage);
            }
        }

        return false;
    }

    /**
     * Validates the form DTO against the passed in entity
     * @param form the form DTO
     * @param entity value obtained after attempting to save via {@link AdminEntityService#updateEntity(EntityForm, String)}
     * @return <b>true</b> if <b>entity</b> does not have any validation errors, <b>false</b> otherwise.
     */
    public boolean validate(EntityForm form, Entity entity, Errors errors) {
        if (entity.isValidationFailure()) {
            return validate(form, entity.getPropertyValidationErrors(), entity.getGlobalValidationErrors(), errors);
        }

        return true;
    }

}