001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.context.support; 018 019import java.text.MessageFormat; 020import java.util.HashMap; 021import java.util.Locale; 022import java.util.Map; 023 024import org.springframework.lang.Nullable; 025import org.springframework.util.Assert; 026 027/** 028 * Simple implementation of {@link org.springframework.context.MessageSource} 029 * which allows messages to be registered programmatically. 030 * This MessageSource supports basic internationalization. 031 * 032 * <p>Intended for testing rather than for use in production systems. 033 * 034 * @author Rod Johnson 035 * @author Juergen Hoeller 036 */ 037public class StaticMessageSource extends AbstractMessageSource { 038 039 private final Map<String, Map<Locale, MessageHolder>> messageMap = new HashMap<>(); 040 041 042 @Override 043 @Nullable 044 protected String resolveCodeWithoutArguments(String code, Locale locale) { 045 Map<Locale, MessageHolder> localeMap = this.messageMap.get(code); 046 if (localeMap == null) { 047 return null; 048 } 049 MessageHolder holder = localeMap.get(locale); 050 if (holder == null) { 051 return null; 052 } 053 return holder.getMessage(); 054 } 055 056 @Override 057 @Nullable 058 protected MessageFormat resolveCode(String code, Locale locale) { 059 Map<Locale, MessageHolder> localeMap = this.messageMap.get(code); 060 if (localeMap == null) { 061 return null; 062 } 063 MessageHolder holder = localeMap.get(locale); 064 if (holder == null) { 065 return null; 066 } 067 return holder.getMessageFormat(); 068 } 069 070 /** 071 * Associate the given message with the given code. 072 * @param code the lookup code 073 * @param locale the locale that the message should be found within 074 * @param msg the message associated with this lookup code 075 */ 076 public void addMessage(String code, Locale locale, String msg) { 077 Assert.notNull(code, "Code must not be null"); 078 Assert.notNull(locale, "Locale must not be null"); 079 Assert.notNull(msg, "Message must not be null"); 080 this.messageMap.computeIfAbsent(code, key -> new HashMap<>(4)).put(locale, new MessageHolder(msg, locale)); 081 if (logger.isDebugEnabled()) { 082 logger.debug("Added message [" + msg + "] for code [" + code + "] and Locale [" + locale + "]"); 083 } 084 } 085 086 /** 087 * Associate the given message values with the given keys as codes. 088 * @param messages the messages to register, with messages codes 089 * as keys and message texts as values 090 * @param locale the locale that the messages should be found within 091 */ 092 public void addMessages(Map<String, String> messages, Locale locale) { 093 Assert.notNull(messages, "Messages Map must not be null"); 094 messages.forEach((code, msg) -> addMessage(code, locale, msg)); 095 } 096 097 098 @Override 099 public String toString() { 100 return getClass().getName() + ": " + this.messageMap; 101 } 102 103 104 private class MessageHolder { 105 106 private final String message; 107 108 private final Locale locale; 109 110 @Nullable 111 private volatile MessageFormat cachedFormat; 112 113 public MessageHolder(String message, Locale locale) { 114 this.message = message; 115 this.locale = locale; 116 } 117 118 public String getMessage() { 119 return this.message; 120 } 121 122 public MessageFormat getMessageFormat() { 123 MessageFormat messageFormat = this.cachedFormat; 124 if (messageFormat == null) { 125 messageFormat = createMessageFormat(this.message, this.locale); 126 this.cachedFormat = messageFormat; 127 } 128 return messageFormat; 129 } 130 131 @Override 132 public String toString() { 133 return this.message; 134 } 135 } 136 137}