/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 com.github.rodionmoiseev.c10n.formatters; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; /** * <p> * A replacement for the default {@link java.text.MessageFormat}-based * message formatting scheme. * * <p> * This formatter allows to use the following types of argument replacement, * assuming a method with 2 arguments: * * <pre> * greet(String name, String surname) * </pre> * * <p> * <em>All examples below evaluate to the same result</em> * <ul> * <li>{@code "Hello {} {}"} - Argument-less placeholders. * First argument-less placeholder is replaced by the first parameter, * second one by the second parameter, and so on.</li> * <li>{@code "Hello {0} {1}"} - Indexed placeholders. Replaced with the argument * value with the same index (0-based).</li> * <li>{@code "Hello {name} {surname}"} - Named placeholders. Replaced with the * argument with the same name (*1). </li> * </ul> * * <p> * <b>*1</b> Named placeholders only work if source was * compiled with {@code -parameters} javac flag. * Alternatively you can use one of {@link NamedArg} * annotations to manually specify parameter names in the source. * Manually specified parameter names do not have to match real parameter names. * For clashing parameter names, behaviour is undefined. */ public class ExtendedMessageFormatter implements MessageFormatter { private final Map<String, String> customReplacements; @SuppressWarnings("unused") public ExtendedMessageFormatter() { this(Collections.<String, String>emptyMap()); } public ExtendedMessageFormatter(Map<String, String> customReplacements) { this.customReplacements = customReplacements; } @Override public String format(Method method, String message, Locale locale, Object... args) { Map<String,String> replacements = new HashMap<String, String>(customReplacements); Parameter[] params = method.getParameters(); Annotation[][] paramAnnotations = method.getParameterAnnotations(); for (int i = 0; i < params.length; i++) { message = message.replaceFirst("\\{\\}", String.format("{%d}", i)); Parameter parameter = params[i]; assert args != null; String value = String.valueOf(args[i]); replacements.put(String.valueOf(i), value); if (parameter.isNamePresent()) { replacements.put(parameter.getName(), value); } Annotation[] annotations = paramAnnotations[i]; for (Annotation annotation : annotations) { if (annotation instanceof NamedArg) { replacements.put(((NamedArg) annotation).value(), value); } } } for (Map.Entry<String, String> entry : replacements.entrySet()) { message = message.replaceAll("\\{" + entry.getKey() + "\\}", entry.getValue()); } return message; } }