All Downloads are FREE. Search and download functionalities are using the official Maven repository.

athenz.shade.zts.org.glassfish.hk2.utilities.general.internal.MessageInterpolatorImpl Maven / Gradle / Ivy

There is a newer version: 1.11.59
Show newest version
/*
 * Copyright (c) 2016, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package athenz.shade.zts.athenz.shade.zts.org.glassfish.hk2.utilities.general.internal;

import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.TreeSet;
import java.util.WeakHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.validation.MessageInterpolator;
import javax.validation.Payload;
import javax.validation.metadata.ConstraintDescriptor;

/**
 * Custom MessageInterpolatorImpl for HK2
 * 

* This message interpolator is different from the default one in the following * ways: *

    *
  1. It uses a class specified in the "payload" argument to the annotation to * find the class loader to find the resource bundle for messages. This allows * classes that are in OSGi modules to specify a resource bundle for bean * validation messages.
  2. * *
  3. The "LocalStrings" resource bundle within the same package as the payload * class is used to search for messages. If a message is not found, the message * is obtained from the default bean validation resource bundle.
  4. *
* This class borrows heavily from the RI implementation of the ResourcBundleMessageInterpolator * authored by Emmanuel Bernard, Hardy Ferentschik, and Gunnar Morling. */ public class MessageInterpolatorImpl implements MessageInterpolator { /** * The name of the default message bundle. */ public static final String DEFAULT_VALIDATION_MESSAGES = "org.hibernate.validator.ValidationMessages"; /** * The name of the user-provided message bundle as defined in the specification. */ public static final String USER_VALIDATION_MESSAGES = "ValidationMessages"; /** * Regular expression used to do message interpolation. */ private static final Pattern MESSAGE_PARAMETER_PATTERN = Pattern.compile("(\\{[^\\}]+?\\})"); /** * The default locale for the current user. */ private final Locale defaultLocale = Locale.getDefault(); /** * Step 1-3 of message interpolation can be cached. We do this in this map. */ private final Map resolvedMessages = new WeakHashMap(); /** * Flag indicating whether this interpolator should chance some of the interpolation steps. */ private final boolean cacheMessages = true; public MessageInterpolatorImpl() { } @Override public String interpolate(String message, Context context) { // probably no need for caching, but it could be done by parameters since the map // is immutable and uniquely built per Validation definition, the comparison has to be based on == and not equals though return interpolate(message, context, defaultLocale); } /** * Runs the message interpolation according to algorithm specified in JSR 303. *
* Note: *
* Look-ups in user bundles is recursive whereas look-ups in default bundle are not! * * @param message the message to interpolate * @param context the context of the annotation which holds the parameters * for which to interpolate this message * @param locale the {@code Locale} to use for the resource bundle. * * @return the interpolated message. */ @Override public String interpolate(String message, Context context, Locale locale) { Map annotationParameters = context.getConstraintDescriptor().getAttributes(); LocalisedMessage localisedMessage = new LocalisedMessage(message, locale); String resolvedMessage = null; if (cacheMessages) { resolvedMessage = resolvedMessages.get(localisedMessage); } // if the message is not already in the cache we have to run step 1-3 of the message resolution if (resolvedMessage == null) { ResourceBundle userResourceBundle = new ContextResourceBundle(context, locale); ClassLoader cl; if (System.getSecurityManager()!=null) { cl = AccessController.doPrivileged(new PrivilegedAction() { @Override public ClassLoader run() { return MessageInterpolator.class.getClassLoader(); } }); } else { cl = MessageInterpolator.class.getClassLoader(); } ResourceBundle defaultResourceBundle = ResourceBundle.getBundle(DEFAULT_VALIDATION_MESSAGES, locale, cl); String userBundleResolvedMessage; resolvedMessage = message; boolean evaluatedDefaultBundleOnce = false; do { // search the user bundle recursive (step1) userBundleResolvedMessage = replaceVariables( resolvedMessage, userResourceBundle, locale, true); // exit condition - we have at least tried to validate against the default bundle and there was no // further replacements if (evaluatedDefaultBundleOnce && !hasReplacementTakenPlace(userBundleResolvedMessage, resolvedMessage)) { break; } // search the default bundle non recursive (step2) resolvedMessage = replaceVariables(userBundleResolvedMessage, defaultResourceBundle, locale, false); evaluatedDefaultBundleOnce = true; if (cacheMessages) { resolvedMessages.put(localisedMessage, resolvedMessage); } } while (true); } // resolve annotation attributes (step 4) resolvedMessage = replaceAnnotationAttributes(resolvedMessage, annotationParameters); // last but not least we have to take care of escaped literals resolvedMessage = resolvedMessage.replace("\\{", "{"); resolvedMessage = resolvedMessage.replace("\\}", "}"); resolvedMessage = resolvedMessage.replace("\\\\", "\\"); return resolvedMessage; } private boolean hasReplacementTakenPlace(String origMessage, String newMessage) { return !origMessage.equals(newMessage); } private String replaceVariables(String message, ResourceBundle bundle, Locale locale, boolean recurse) { Matcher matcher = MESSAGE_PARAMETER_PATTERN.matcher(message); StringBuffer sb = new StringBuffer(); String resolvedParameterValue; while (matcher.find()) { String parameter = matcher.group(1); resolvedParameterValue = resolveParameter( parameter, bundle, locale, recurse); matcher.appendReplacement(sb, escapeMetaCharacters(resolvedParameterValue)); } matcher.appendTail(sb); return sb.toString(); } private String replaceAnnotationAttributes(String message, Map annotationParameters) { Matcher matcher = MESSAGE_PARAMETER_PATTERN.matcher(message); StringBuffer sb = new StringBuffer(); while (matcher.find()) { String resolvedParameterValue; String parameter = matcher.group(1); Object variable = annotationParameters.get(removeCurlyBrace(parameter)); if (variable != null) { resolvedParameterValue = escapeMetaCharacters(variable.toString()); } else { resolvedParameterValue = parameter; } matcher.appendReplacement(sb, resolvedParameterValue); } matcher.appendTail(sb); return sb.toString(); } private String resolveParameter(String parameterName, ResourceBundle bundle, Locale locale, boolean recurse) { String parameterValue; try { if (bundle != null) { parameterValue = bundle.getString(removeCurlyBrace(parameterName)); if (recurse) { parameterValue = replaceVariables(parameterValue, bundle, locale, recurse); } } else { parameterValue = parameterName; } } catch (MissingResourceException e) { // return parameter itself parameterValue = parameterName; } return parameterValue; } private String removeCurlyBrace(String parameter) { return parameter.substring(1, parameter.length() - 1); } /** * @param s The string in which to replace the meta characters '$' and '\'. * * @return A string where meta characters relevant for {@link Matcher#appendReplacement} are escaped. */ private String escapeMetaCharacters(String s) { String escapedString = s.replace("\\", "\\\\"); escapedString = escapedString.replace("$", "\\$"); return escapedString; } /** * A resource bundle that takes strings from the annotation context. This class * looks for the "LocalStrings" resource bundle in the same package as the * class that is specified in the payload of the annotation. If a String * cannot be found there, then it looks in the user resource bundle as defined * in JSR-303. */ private static class ContextResourceBundle extends ResourceBundle { ResourceBundle contextBundle; ResourceBundle userBundle; ContextResourceBundle(Context context, Locale locale) { ConstraintDescriptor descriptor = context.getConstraintDescriptor(); Set> payload = descriptor.getPayload(); // Load the LogStrings.properties for the argument Locale, from the ClassLoader // of the payload. if (!payload.isEmpty()) { final Class payloadClass = payload.iterator().next(); String baseName = payloadClass.getPackage().getName() + ".LocalStrings"; ClassLoader cl; if (System.getSecurityManager()!=null) { cl = AccessController.doPrivileged(new PrivilegedAction() { @Override public ClassLoader run() { return payloadClass.getClassLoader(); } }); } else { cl = payloadClass.getClassLoader(); } try { contextBundle = ResourceBundle.getBundle(baseName, locale, cl); } catch (MissingResourceException mre) { contextBundle = null; } } try { ClassLoader cl = System.getSecurityManager()==null?Thread.currentThread().getContextClassLoader(): AccessController.doPrivileged(new PrivilegedAction() { @Override public ClassLoader run() { return Thread.currentThread().getContextClassLoader(); } }); userBundle = ResourceBundle.getBundle(USER_VALIDATION_MESSAGES, locale, cl); } catch (MissingResourceException mre) { userBundle = null; } if (userBundle != null) { setParent(userBundle); } } @Override protected Object handleGetObject(String key) { if (contextBundle != null) { return contextBundle.getObject(key); } return null; } @Override public Enumeration getKeys() { Set keys = new TreeSet(); if (contextBundle != null) { keys.addAll(Collections.list(contextBundle.getKeys())); } if (userBundle != null) { keys.addAll(Collections.list(userBundle.getKeys())); } return Collections.enumeration(keys); } } private static class LocalisedMessage { private final String message; private final Locale locale; LocalisedMessage(String message, Locale locale) { this.message = message; this.locale = locale; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } LocalisedMessage that = (LocalisedMessage) o; if (locale != null ? !locale.equals(that.locale) : that.locale != null) { return false; } if (message != null ? !message.equals(that.message) : that.message != null) { return false; } return true; } @Override public int hashCode() { int result = message != null ? message.hashCode() : 0; result = 31 * result + (locale != null ? locale.hashCode() : 0); return result; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy