/* ================================================================== * TemplatedMessageSource.java - Jul 22, 2013 3:27:31 PM * * Copyright 2007-2012 SolarNetwork.net Dev Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA * ================================================================== * $Id$ * ================================================================== */ package net.solarnetwork.node.util; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.springframework.context.HierarchicalMessageSource; import org.springframework.context.MessageSource; import org.springframework.context.MessageSourceResolvable; import org.springframework.context.NoSuchMessageException; /** * Delegating {@link MessageSource} that dynamically extracts a pre-configured * regular expression match from all message codes. * * <p> * The inspiration for this class was to support messages for objects that might * be nested in other objects used in * {@link net.solarnetwork.node.settings.SettingSpecifierProvider} * implementations. When one provider proxies another, or uses nested bean * paths, this class can be used to dynamically re-map message codes. For * example a code <code>mapProperty['url']</code> could be re-mapped to * <code>url</code>. * </p> * * <p> * The configurable properties of this class are: * </p> * * <dl class="class-properties"> * <dt>regex</dt> * <dd>The regular expression to match against message codes. The regular * expression must provide at least one capture group; all capture groups are * combined into the final message code.</dd> * * <dt>delegate</dt> * <dd>The {@link MessageSource} to delegate to. If that object implements * {@link HierarchicalMessageSource} then those methods will be supported by * instances of this class as well.</dd> * </dl> * * @author matt * @version 1.0 */ public class TemplatedMessageSource implements MessageSource, HierarchicalMessageSource { private String regex; private Pattern pat; private MessageSource delegate; @Override public void setParentMessageSource(MessageSource parent) { if ( delegate instanceof HierarchicalMessageSource ) { ((HierarchicalMessageSource) delegate).setParentMessageSource(parent); } else { throw new UnsupportedOperationException( "Delegate does not implement HierarchicalMessageSource"); } } @Override public MessageSource getParentMessageSource() { if ( delegate instanceof HierarchicalMessageSource ) { return ((HierarchicalMessageSource) delegate).getParentMessageSource(); } throw new UnsupportedOperationException("Delegate does not implement HierarchicalMessageSource"); } @Override public String getMessage(String code, Object[] args, String defaultMessage, Locale locale) { if ( pat != null ) { Matcher m = pat.matcher(code); final int count = m.groupCount(); if ( m.matches() && count > 0 ) { // remap using regex capture groups StringBuilder buf = new StringBuilder(); for ( int i = 1; i <= count; i++ ) { buf.append(m.group(i)); } code = buf.toString(); } } return delegate.getMessage(code, args, defaultMessage, locale); } @Override public String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException { if ( pat != null ) { Matcher m = pat.matcher(code); final int count = m.groupCount(); if ( m.matches() && count > 0 ) { // remap using regex capture groups StringBuilder buf = new StringBuilder(); for ( int i = 1; i <= count; i++ ) { buf.append(m.group(i)); } code = buf.toString(); } } return delegate.getMessage(code, args, locale); } @Override public String getMessage(final MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException { final String[] codes = resolvable.getCodes(); if ( pat != null ) { for ( int i = 0; i < codes.length; i++ ) { Matcher m = pat.matcher(codes[i]); final int count = m.groupCount(); if ( m.matches() && count > 0 ) { // remap using regex capture group StringBuilder buf = new StringBuilder(); for ( int j = 1; j <= count; j++ ) { buf.append(m.group(j)); } codes[i] = buf.toString(); } } } return delegate.getMessage(new MessageSourceResolvable() { @Override public String getDefaultMessage() { return resolvable.getDefaultMessage(); } @Override public String[] getCodes() { return codes; } @Override public Object[] getArguments() { return resolvable.getArguments(); } }, locale); } public String getRegex() { return regex; } public void setRegex(String regex) { this.regex = regex; if ( regex != null && regex.length() > 0 ) { pat = Pattern.compile(regex); } else { pat = null; } } public MessageSource getDelegate() { return delegate; } public void setDelegate(MessageSource delegate) { this.delegate = delegate; } }