/* * 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.validator; import static javax.validation.Validation.byDefaultProvider; import static org.omnifaces.util.Components.getCurrentComponent; import static org.omnifaces.util.Components.getLabel; import static org.omnifaces.util.Faces.getLocale; import static org.omnifaces.util.Faces.hasContext; import java.util.Locale; import javax.validation.MessageInterpolator; /** * <p> * Unlike native JSF validation error messages, in a bean validation message by default the label of the component where * a validation constraint violation originated from can not be displayed in the middle of a message. Using the * <code>javax.faces.validator.BeanValidator.MESSAGE</code> bundle key such label can be put in front or behind the * message, but that's it. With this {@link JsfLabelMessageInterpolator} a label can appear in the middle of a message, * by using the special placeholder <code>{jsf.label}</code> in bean validation messages. * <p> * Note that Bean Validation is not only called from within JSF, and as such JSF might not be available. If JSF * is not available occurrences of <code>{jsf.label}</code> will be replaced by an empty string. The user should take * care that messages are compatible with both situations if needed. * <p> * This message interpolator is <strong>not</strong> needed for putting a component label before or after a bean * validation message. That functionality is already provided by JSF itself via the * <code>javax.faces.validator.BeanValidator.MESSAGE</code> key in any resource bundle known to JSF. * * <h3>Installation</h3> * <p> * Create a <code>/META-INF/validation.xml</code> file in WAR with the following contents: * * <pre> * <?xml version="1.0" encoding="UTF-8"?> * <validation-config * xmlns="http://jboss.org/xml/ns/javax/validation/configuration" * xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" * xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/configuration validation-configuration-1.0.xsd" * > * <message-interpolator>org.omnifaces.validator.JsfLabelMessageInterpolator</message-interpolator> * </validation-config> * </pre> * * <h3>Usage</h3> * <p>As an example, the customization of <code>@Size</code> in <code>ValidationMessages.properties</code>: * <pre> * javax.validation.constraints.Size.message = The size of {jsf.label} must be between {min} and {max} characters * </pre> * * @author Arjan Tijms * @since 1.5 */ public class JsfLabelMessageInterpolator implements MessageInterpolator { private final MessageInterpolator wrapped; public JsfLabelMessageInterpolator() { wrapped = byDefaultProvider().configure().getDefaultMessageInterpolator(); } @Override public String interpolate(String messageTemplate, Context context) { return interpolate(messageTemplate, context, hasContext() ? getLocale() : Locale.getDefault()); } @Override public String interpolate(String messageTemplate, Context context, Locale locale) { String message = wrapped.interpolate(messageTemplate, context, locale); if (message.contains("{jsf.label}")) { String label = ""; if (hasContext()) { label = getLabel(getCurrentComponent()); } message = message.replace("{jsf.label}", label); } return message; } }