/*
* Copyright (C) 2009 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.commons.utils;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Benjamin Mestrallet benjamin.mestrallet@exoplatform.com
*/
public class MapResourceBundle extends ResourceBundle implements Serializable
{
/**
* The serial version UID
*/
private static final long serialVersionUID = -7020823660841958748L;
private final static Pattern PATTERN = Pattern.compile("#\\{.*\\}");
private Map<String, String> props;
private Locale locale;
public MapResourceBundle(Locale l)
{
this.locale = l;
this.props = new HashMap<String, String>();
}
public MapResourceBundle(ResourceBundle rB, Locale l)
{
Map<String, String> props = new HashMap<String, String>();
doMerge(props, rB);
//
this.locale = l;
this.props = props;
}
private static void doMerge(Map<String, String> props, ResourceBundle rB)
{
Enumeration<String> e = rB.getKeys();
while (e.hasMoreElements())
{
String key = e.nextElement();
if (props.get(key) == null)
{
Object o = rB.getObject(key);
if (o instanceof String)
{
String value = (String)o;
props.put(key.intern(), value.intern());
}
}
}
}
protected Object handleGetObject(String key)
{
return props.get(key);
}
public Enumeration<String> getKeys()
{
final Iterator<String> i = props.keySet().iterator();
return new Enumeration<String>()
{
public boolean hasMoreElements()
{
return i.hasNext();
}
public String nextElement()
{
return i.next();
}
};
}
public Locale getLocale()
{
return this.locale;
}
public void add(String key, Object o)
{
if (key != null && o instanceof String)
{
String value = (String)o;
props.put(key.intern(), value.intern());
}
}
public void remove(String key)
{
if (key != null)
{
props.remove(key);
}
}
public void merge(ResourceBundle bundle)
{
doMerge(props, bundle);
}
public void resolveDependencies()
{
Map<String, String> tempMap = new HashMap<String ,String>(props);
for (String element : props.keySet())
{
String value = lookupKey(tempMap, element, new HashSet<String>());
if (value != null)
{
tempMap.put(element.intern(), value.intern());
}
}
props = tempMap;
}
private String lookupKey(Map<String, String> props, String key, Set<String> callStack)
{
String s = props.get(key);
if (s == null || callStack.contains(key))
{
// The value cannot be found or it has already been asked which means that
// a loop has been detected
return key;
}
callStack.add(key);
Matcher matcher = PATTERN.matcher(s);
if (matcher.find())
{
return recursivedResolving(props, s, callStack);
}
// The value could be resolved thus it can be removed from the callStack
callStack.remove(key);
return s;
}
private String recursivedResolving(Map props, String value, Set<String> callStack)
{
String resolved = value;
StringBuilder sB = new StringBuilder();
while (resolved.indexOf("#{") != -1)
{
sB.setLength(0);
int firstIndex = resolved.indexOf('#');
int lastIndex = resolved.indexOf('}', firstIndex);
String realKey = resolved.substring(firstIndex + 2, lastIndex);
sB.append(resolved.substring(0, firstIndex));
sB.append(lookupKey(props, realKey, callStack));
sB.append(resolved.substring(lastIndex + 1));
resolved = sB.toString();
}
return resolved;
}
}