/* * Copyright 2017 OmniFaces * * 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. */ package org.omnifaces.eventlistener; import static java.lang.String.format; import static java.util.logging.Level.FINER; import java.util.logging.Logger; import javax.faces.component.EditableValueHolder; import javax.faces.component.UICommand; import javax.faces.component.UIInput; import javax.faces.event.ComponentSystemEvent; import javax.faces.event.PostValidateEvent; import javax.faces.event.PreValidateEvent; import javax.faces.event.SystemEvent; import javax.faces.event.SystemEventListener; import javax.faces.validator.BeanValidator; import javax.faces.validator.Validator; /** * Overrides {@link BeanValidator#setValidationGroups(String)} for all components in the current view. This allows to * temporarily use different validationGroups or disabling validation if a specific {@link UICommand} or {@link UIInput} * has invoked the form submit. * * @author Adrian Gygax * @author Bauke Scholtz * @since 1.3 */ public class BeanValidationEventListener implements SystemEventListener { // Constants ------------------------------------------------------------------------------------------------------ private static final String ATTRIBUTE_ORIGINAL_VALIDATION_GROUPS = "BeanValidationEventListener.originalValidationGroups"; private static final Logger LOGGER = Logger.getLogger(BeanValidationEventListener.class.getName()); private static final String LOG_VALIDATION_GROUPS_OVERRIDDEN = "Validation groups for component with id '%s' overriden from '%s' to '%s'"; // Variables ------------------------------------------------------------------------------------------------------ private String validationGroups; private boolean disabled; // Constructors --------------------------------------------------------------------------------------------------- /** * Construct an instance of bean validation event listener based on the given validation groups and disabled state. * @param validationGroups The validation groups. * @param disabled The disabled state. */ public BeanValidationEventListener(String validationGroups, boolean disabled) { this.validationGroups = validationGroups; this.disabled = disabled; } // Actions -------------------------------------------------------------------------------------------------------- /** * Only listens to {@link UIInput} components which have a {@link javax.faces.validator.BeanValidator} assigned. */ @Override public boolean isListenerForSource(Object source) { return source instanceof UIInput && getBeanValidator((EditableValueHolder) source) != null; } /** * Handle the {@link PreValidateEvent} and {@link PostValidateEvent}. */ @Override public void processEvent(SystemEvent event) { if (event instanceof PreValidateEvent) { handlePreValidate((UIInput) ((ComponentSystemEvent) event).getComponent()); } else if (event instanceof PostValidateEvent) { handlePostValidate((UIInput) ((ComponentSystemEvent) event).getComponent()); } } /** * Replaces the original value of {@link BeanValidator#getValidationGroups()} with the value from the tag attribute. */ private void handlePreValidate(UIInput component) { BeanValidator beanValidator = getBeanValidator(component); if (beanValidator == null) { return; } String newValidationGroups = disabled ? NoValidationGroup.class.getName() : validationGroups; String originalValidationGroups = beanValidator.getValidationGroups(); if (originalValidationGroups != null) { component.getAttributes().put(ATTRIBUTE_ORIGINAL_VALIDATION_GROUPS, originalValidationGroups); } beanValidator.setValidationGroups(newValidationGroups); if (LOGGER.isLoggable(FINER)) { LOGGER.finer(format(LOG_VALIDATION_GROUPS_OVERRIDDEN, component.getClientId(), originalValidationGroups, newValidationGroups)); } } /** * Restores the original value of {@link BeanValidator#getValidationGroups()}. */ private void handlePostValidate(UIInput component) { BeanValidator beanValidator = getBeanValidator(component); if (beanValidator != null) { String originalValidationGroups = (String) component.getAttributes().remove(ATTRIBUTE_ORIGINAL_VALIDATION_GROUPS); beanValidator.setValidationGroups(originalValidationGroups); } } // Helpers -------------------------------------------------------------------------------------------------------- /** * Obtain the bean validator instance of the given editable value holder component. */ private static BeanValidator getBeanValidator(EditableValueHolder component) { Validator[] validators = component.getValidators(); for (Validator validator : validators) { if (validator instanceof BeanValidator) { return (BeanValidator) validator; } } return null; } /** * Dummy validation group to disable any validation. */ private interface NoValidationGroup { // } }