/** * */ package net.varkhan.base.management.config; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * <b>Map-based thread-safe configurations.</b> * <p/> * * @author varkhan * @date Sep 30, 2010 * @time 5:57:33 AM */ public class MapConfiguration implements SettableConfiguration { /** * An internal map { key -> val } for known default entries */ protected final ConcurrentMap<String,Object> defentries=new ConcurrentHashMap<String,Object>(); /** * An internal map { ctx -> { key -> val } } for known contextualized entries */ protected final ConcurrentMap<String,ConcurrentMap<String,Object>> ctxentries=new ConcurrentHashMap<String,ConcurrentMap<String,Object>>(); /** * Create an empty map config. * */ public MapConfiguration() { } /** * Create a map config. * * @param defs a list of { context name, config key, config value } triplets, where * each context name and config key must be a String. */ public MapConfiguration(Object... defs) { // No prop defs? we are done if(defs==null || defs.length==0) return; if((defs.length%3)!=0) throw new IllegalArgumentException("Contextual properties definition array must have a multiple of 3 number of elements"); for(int i=0;i+2<defs.length;i+=3) { if(!(defs[i] instanceof CharSequence)) throw new IllegalArgumentException("Context names must be strings"); if(!(defs[i+1] instanceof CharSequence)) throw new IllegalArgumentException("Config keys must be strings"); String ctx=defs[i]==null?"":defs[i].toString(); String key=defs[i+1].toString(); Object val=defs[i+2]; if(ctx==null || ctx.length()==0) add(key, val); else add(ctx, key, val); } } @Override public Object get(String ctx, String key) { // Get context to key map Map<String,Object> k2e; if(ctx==null||ctx.length()==0) k2e=defentries; else { k2e=ctxentries.get(ctx); if(k2e==null) k2e=defentries; } // Attempt to find exact key, and return value or null if not found return k2e.get(key); } @Override public Iterable<String> contexts() { return new Iterable<String>() { public Iterator<String> iterator() { return new Iterator<String>() { private boolean first=true; private final Iterator<String> it=ctxentries.keySet().iterator(); public boolean hasNext() { return first||it.hasNext(); } public String next() { if(first) { first=false; return ""; } return it.next(); } public void remove() { if(!first) it.remove(); } }; } }; } @Override public SettableConfiguration.Context context(final String ctx) { if(ctx==null||ctx.length()==0) return new MapContext("",defentries); ConcurrentMap<String,Object> k2e=ctxentries.get(ctx); if(k2e==null) { k2e=new ConcurrentHashMap<String,Object>(); ConcurrentMap<String,Object> k2ex=ctxentries.putIfAbsent(ctx, k2e); if(k2ex!=null) k2e=k2ex; } return new MapContext(ctx, k2e); } @Override public void add(String key, Object val) { defentries.put(key, val); } @Override public void add(String ctx, String key, Object val) { ConcurrentMap<String,Object> k2e; if(ctx==null||ctx.length()==0) k2e=defentries; else { k2e=ctxentries.get(ctx); if(k2e==null) { k2e=new ConcurrentHashMap<String,Object>(); ConcurrentMap<String,Object> k2ex=ctxentries.putIfAbsent(ctx, k2e); if(k2ex!=null) k2e=k2ex; } } k2e.put(key, val); } @Override public void add(Configuration.Entry ent) { add(ent.ctx(), ent.key(), ent.value()); } @Override public void loadConfig(Configuration cfg) { for(String ctx: cfg.contexts()) { for(Configuration.Entry ent: cfg.context(ctx)) { add(ent); } } } @Override public void loadConfig(String ctx, Map<String,?> props) { for(Map.Entry<String,?> prop: props.entrySet()) { if(prop.getValue()!=null) add(ctx, prop.getKey(), prop.getValue().toString()); } } }