/**
*
*/
package net.varkhan.base.management.logging;
import net.varkhan.base.management.config.*;
import java.util.*;
/**
* <b>Immutable property-based lookup for logging configurations.</b>
* <p/>
*
* @author varkhan
* @date Sep 30, 2010
* @time 5:57:33 AM
*/
public class LogConfigProps implements LogConfig {
protected final SettableConfiguration config;
protected final String[] levels;
protected final Map<String,Long> levcache=new HashMap<String,Long>();
/**
* Create a property based log config.
*
* @param levels the symbolic label definitions
* @param defcfg a default configuration set
* @param propdefs a list of { context name, filter key, logging mask } triplets
*/
@SuppressWarnings({ "unchecked" })
public LogConfigProps(String[] levels, Configuration defcfg, Object... propdefs) throws ConfigurationError {
this(levels, defcfg);
// No prop defs? we are done
if(propdefs==null || propdefs.length==0) return;
if((propdefs.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<propdefs.length;i+=3) {
String ctx=propdefs[i]==null?"":propdefs[i].toString();
String key=propdefs[i+1].toString();
Object val=propdefs[i+2];
if(!(propdefs[i] instanceof CharSequence))
throw new IllegalArgumentException("Context names must be strings");
if(!(propdefs[i+1] instanceof CharSequence))
throw new IllegalArgumentException("Filter keys must be strings");
if(!(propdefs[i+2] instanceof Number) && !(propdefs[i+2] instanceof CharSequence))
throw new ConfigurationError("Logging masks must be numbers or strings",ctx,key,val);
if(ctx==null || ctx.length()==0) config.add(key, parseLevelMask(val));
else config.add(ctx, key, parseLevelMask(val));
}
}
/**
* Create a property based log config.
*
* @param levels the symbolic label definitions
* @param defcfg a default configuration set
*/
public LogConfigProps(String[] levels, Configuration defcfg) {
this(levels, defcfg,new MapConfiguration());
}
/**
* Create a property based log config.
*
* @param levels the symbolic label definitions
* @param defcfg a default configuration set
* @param setcfg an overriding, modifiable configuration set
*/
public LogConfigProps(String[] levels, Configuration defcfg, SettableConfiguration setcfg) {
this.levels = levels.clone();
this.config = defcfg==null?setcfg:new OverrideConfiguration(setcfg,defcfg);
}
public long getLevelMask(String ctx, String key) {
Object val = config.get(ctx, key);
if(val==null) return 0L;
return parseLevelMask(val.toString());
}
protected long parseLevelMask(Object val) {
if(val==null) return 0L;
if(val instanceof Number) return ((Number)val).longValue()|SET_MARKER;
String str = val.toString();
Long lev=levcache.get(str);
if(lev==null) {
lev=parseLevelMask(levels, str);
levcache.put(str, lev);
}
return lev|SET_MARKER;
}
public boolean isLevelEnabled(String ctx, String key, int lev) {
return lev<SET_BITPOS && (getLevelMask(ctx, key)&(1<<lev))!=0;
}
public Iterable<String> contexts() {
return config.contexts();
}
public Iterable<Level> levels(final String ctx) {
return new Iterable<Level>() {
protected final Iterable<Configuration.Entry> itb=config.context(ctx);
public Iterator<Level> iterator() {
return new Iterator<Level>() {
protected final Iterator<Configuration.Entry> itr=itb.iterator();
public boolean hasNext() { return itr.hasNext(); }
public Level next() {
final Configuration.Entry ent=itr.next();
return new Level() {
public String ctx() { return ent.ctx(); }
public String key() { return ent.key(); }
public long mask() {
Object val=ent.value();
return val==null ? 0L : parseLevelMask(val);
}
};
}
public void remove() { itr.remove(); }
};
}
};
}
public void setLevelMask(String ctx, String key, long lev) {
config.add(ctx, key, lev);
}
public void setLevelMask(Level lev) {
setLevelMask(lev.ctx(), lev.key(), lev.mask());
}
public void loadConfig(LogConfig cfg) {
for(String ctx : cfg.contexts()) {
for(Level lev : cfg.levels(ctx)) {
setLevelMask(lev);
}
}
}
public void loadConfig(String ctx, Map<String,Object> props) {
for(Map.Entry<String,Object> prop : props.entrySet()) {
if(prop.getValue()==null) setLevelMask(ctx, prop.getKey(), 0);
else setLevelMask(ctx, prop.getKey(), parseLevelMask(prop.getValue()));
}
}
/**
* Parse a level set definition
*
* @param lev an array of symbolic level specifications
* @param val either a bit mask, or a comma or pipe separated set of enabled levels
*
* @return either the specified bit mask (parsed in, respectively, base 16, 8 or 2 if it is prefixed by the character X, O or B, or by default in decimal)
* or the mask whose bits are set for each numerical level specification, and for each position in the identifier array of a symbolic specification
*/
public static long parseLevelMask(String[] lev, String val) {
if(val==null||val.length()==0||"0".equals(val)) return 0;
try {
switch(val.charAt(0)) {
case 'X':
return Long.parseLong(val.substring(1), 16);
case 'O':
return Long.parseLong(val.substring(1), 8);
case '0':
return Long.parseLong(val.substring(1), 8);
case 'B':
return Long.parseLong(val.substring(1), 2);
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return Long.parseLong(val);
}
}
catch(Exception e) {
// ignore, and go on to parse set
}
Set<String> flags=new HashSet<String>(lev.length);
long mask=0;
int j=-1;
for(int i=0;i<val.length();i++) {
char c=val.charAt(i);
if(c==','||c=='|'||c==';') {
String f=val.substring(j+1, i);
try {
mask|=1<<Long.parseLong(f);
}
catch(NumberFormatException e) {
flags.add(f);
}
j=i;
}
}
if(j<val.length()) {
String f=val.substring(j+1);
try {
mask|=1<<Long.parseLong(f);
}
catch(NumberFormatException e) {
flags.add(f);
}
}
if(!flags.isEmpty()&&lev!=null) for(int i=0;i<lev.length;i++) {
if(flags.contains(lev[i])) mask|=i<<i;
}
return mask;
}
}