/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package net.sf.jiga.xtended.kernel;
import java.io.PrintStream;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import net.sf.jiga.xtended.JXAException;
import net.sf.jiga.xtended.ui.Ant;
/**
*
*
* This maps all registered Debugger's management, so that enabling
* JXAenvUtils._debug can be reflected to all registered Debugger's, also it can
* dis/enable each Debugger seperately.
* {@linkplain #associateJXADebug(Class)}, {@linkplain #associateJXADebugSys(Class)}
* or {@linkplain #associateDebugLevel(Class)} to initialize debugging level for
* a defined Class
* {@linkplain #setDebugLevelEnabled(boolean, Class)}, {@linkplain #setJXADebugSysEnabled(boolean, Class)}
* or {@linkplain #setDebuggerEnabled(boolean, Class, Level)} to enable/disable
* debug level NOTICE : methods named *Debug* are related to jxa.debug property,
* and those named *DebugSys* to jxa.debugSys property. The *Debugger* ones are
* for Level's of debugging generated by {@linkplain #newDebugLevel()}.
*
* @author www.b23prodtm.info
*
*/
public class DebugMap {
private class SoftDebugger extends SoftReference {
final String className;
/**
*
*/
public SoftDebugger(Class<? extends Debugger> d) {
super(d, refQueue);
this.className = d.getName();
}
}
private BigBitStack bits = new BigBitStack();
private Level jxaDebug = newDebugLevel();
private Level jxaDebugSys = newDebugLevel();
/**
*
* @see Ant#JXA_DEBUG_VOID
*/
public Level _VOID = newDebugLevel(), _SYS = jxaDebugSys, _JXA = jxaDebug;
/**
* global debugger bitmap
*/
private Level bitMap = Level.NoDebug;
/**
* running classes
*/
private Map<String, SoftDebugger> instances = Collections.synchronizedMap(new HashMap<String, SoftDebugger>());
/**
* associated classes to level
*/
private Map<String, Level> instancesClass = Collections.synchronizedMap(new HashMap<String, Level>());
private ReferenceQueue refQueue = new ReferenceQueue();
private DebugMap() {
}
private static DebugMap _instance = _getInstance();
private Level getJXADebugState() {
return (JXAenvUtils._debug ? jxaDebug : Level.NoDebug).or(JXAenvUtils._debugSys ? jxaDebugSys : Level.NoDebug).or(JXAenvUtils._getSysBoolean(Ant.JXA_DEBUG_VOID) ? _VOID : Level.NoDebug);
}
private void cleanup() {
SoftDebugger sd = null;
while ((sd = (SoftDebugger) refQueue.poll()) != null) {
instances.remove(sd.className);
instancesClass.remove(sd.className);
}
}
/**
* whole associated bitmap for that debugger
*/
private Level bitMap(Class<? extends Debugger> d) {
Level lvl = instancesClass.get(d.getName());
if (lvl != null) {
return lvl;
} else {
return Level.NoDebug;
}
}
/**
* associated bits for that debugger and level
*/
private Level levelBits(Class<? extends Debugger> d, Level level) {
Level lvl = bitMap(d);
return lvl.and(new Level(bits._getMask(level.n)).or(level));
}
/**
* defined level for debugger
*
* @return true if and only if the debugger is associated to Level and the
* Debugger has not been disabled
*/
private boolean isDebuggerEnabled(Class<? extends Debugger> d, Level level) {
Level b = levelBits(d, level);
/*
* compare associated bitmap against current state bitmap
*/
Level r = bitMap.or(getJXADebugState()).and(b);
/*
* either associated level is enabled or the debugger is enabled with
* that level
*/
return !r.equals(Level.NoDebug);
}
/**
* defined level
*
* @param level
* @return
*/
public boolean isDebugLevelEnabled(Level level) {
return bitMap.or(getJXADebugState()).and(level).equals(level);
}
/**
* specified debugger only (on associated level if it exists)
*
* @param enable
* @param d
*/
public void setDebuggerEnabled(boolean enable, Class<? extends Debugger> d) {
Level level = bitMap(d);
setDebuggerEnabled(enable, d, level.equals(Level.NoDebug) ? newDebugLevel() : level);
}
/**
* defines the debugger at specified level
*
* @param enable
* @param d
* @param level
*/
public void setDebuggerEnabled(boolean enable, Class<? extends Debugger> d, Level level) {
Level lvl = Level.NoDebug;
cleanup();
assert !new Level(bits._getAllBitRanges()).and(level).equals(Level.NoDebug) : "no valid level (use newDebugLevel() as level)";
/**
* just add/remove the "finger-print" of this Debugger according to the
* selected level in bitmap
*/
if (enable) {
associateDebugLevel(d, level);
lvl = levelBits(d, level);
bitMap = bitMap.or(new Level(bits._getAllBits()).and(lvl));
if (isDebugLevelEnabled(_VOID)) {
System.out.println("enabled debugger " + d.getSimpleName());
}
} else {
deassociateDebugLevel(d, level);
lvl = levelBits(d, level);
bitMap = bitMap.andNot(new Level(bits._getAllBits()).and(lvl));
if (isDebugLevelEnabled(_VOID)) {
System.out.println("disabled debugger " + d.getSimpleName());
}
}
}
/**
* dis/enable a defined level
*
* @param enable
* @param level
*/
public void setDebugLevelEnabled(boolean enable, Level level) {
/**
* add/remove the level range in bitmap
*/
if (enable) { /*
* enable Level bit
*/
bitMap = bitMap.or(level.and(new Level(bits._getAllBitRanges())));
if (isDebugLevelEnabled(_VOID)) {
System.out.println("enabled debuglevel " + level.toString());
}
} else { /*
* remove whole level and associated (mask) bits
*/
bitMap = bitMap.andNot(new Level(bits._getMask(level.n)).or(level));
}
if (isDebugLevelEnabled(_VOID)) {
System.out.println("disabled debuglevel " + level.toString());
}
}
/**
* @return
*/
public final static DebugMap _getInstance() {
return _instance != null ? _instance : (_instance = new DebugMap());
}
/**
* defines a new custom level (store it as a final int !)
*
* @return
*/
public Level newDebugLevel() {
return newDebugLevel(null);
}
/**
* defines a new custom level (store it as a final int !) and associate the
* specified Class to it, so that whenever the debugLevel is enabled, this
* class is set as debugged too.
*
* @return
*/
public Level newDebugLevel(Class<? extends Debugger> clazz) {
Level l = new Level(bits._newBitRange());
if (clazz != null) {
associateDebugLevel(clazz, l);
}
return l;
}
/**
* JXAenvUtils._debug level
*
* @param d
* @return
* @deprecated #isJXADebugEnabled(Class)
*/
public boolean isJXADebugEnabled(Debugger d) {
return isDebuggerEnabled(d.getClass(), jxaDebug);
}
/**
* JXAenvUtils._debugSys level
*
* @param d
* @return
* @deprecated #isJXADebugSysEnabled(Class)
*/
public boolean isJXADebugSysEnabled(Debugger d) {
return isDebuggerEnabled(d.getClass(), jxaDebugSys);
}
/**
* JXAenvUtils._debug level
*
* @param b
* @param d
*/
public void setJXADebugEnabled(boolean b, Class<? extends Debugger> d) {
setDebuggerEnabled(b, d, jxaDebug);
}
/**
* JXAenvUtils._debugSys level
*
* @param b
* @param d
*/
public void setJXADebugSysEnabled(boolean b, Class<? extends Debugger> d) {
setDebuggerEnabled(b, d, jxaDebugSys);
}
/**
* JXAenvUtils._debug level
*
* @param b
*/
public void setJXADebugEnabled(boolean b) {
setDebugLevelEnabled(b, jxaDebug);
}
/**
* JXAenvUtils._debugSys level
*
* @param b
*/
public void setJXADebugSysEnabled(boolean b) {
setDebugLevelEnabled(b, jxaDebugSys);
}
/**
* JXAenvUtils._debug level
*
* @return
*/
public boolean isJXADebugEnabled() {
return isDebugLevelEnabled(jxaDebug);
}
/**
* JXAenvUtils._debugSys level
*
* @return
*/
public boolean isJXADebugSysEnabled() {
return isDebugLevelEnabled(jxaDebugSys);
}
/**
* JXAenvUtils._debug level
*
* @param d
* @return
*/
public boolean isJXADebugEnabled(Class<? extends Debugger> d) {
return isDebuggerEnabled(d, jxaDebug);
}
/**
* JXAenvUtils._debugSys level
*
* @param d
* @return
*/
public boolean isJXADebugSysEnabled(Class<? extends Debugger> d) {
return isDebuggerEnabled(d, jxaDebugSys);
}
/**
* any level
*
* @param d
* @return
*/
public boolean isDebuggerEnabled(Class<? extends Debugger> d) {
return isDebuggerEnabled(d, new Level(bits._getAllBitRanges()));
}
/**
* will dis/enable Debugger at JXAenvUtils._debug level it registers the
* Debugger and links it to the {@linkplain JXAenvUtils#_debug} state
*
* @param enable
* @param d
* @deprecated #setJXADebugEnabled(boolean, Class)
*/
public void setJXADebugEnabled(boolean enable, Debugger d) {
setDebuggerEnabled(enable, d.getClass(), jxaDebug);
}
/**
* will dis/enable Debugger at JXAenvUtils._debugSys level it registers
* Debugger and links it to the {@linkplain JXAenvUtils#_debugSys} (kernel
* debug) state
*
* @param b
* @param d
* @deprecated #setJXADebugSysEnabled(boolean, Class)
*/
public void setJXADebugSysEnabled(boolean b, Debugger d) {
setDebuggerEnabled(b, d.getClass(), jxaDebugSys);
}
/**
* @param d
*/
public void associateJXADebugSys(Class<? extends Debugger> d) {
associateDebugLevel(d, jxaDebugSys);
}
/**
* @param d
*/
public void associateJXADebug(Class<? extends Debugger> d) {
associateDebugLevel(d, jxaDebug);
}
private Set<Level> availableBits = Collections.synchronizedSet(new HashSet<Level>());
/**
* @return an available level bit or null if none can be found for the level
* range
*/
private Level findVacantLevelBits(Level level) {
synchronized(availableBits) {
for (Level l : availableBits) {
if (level.and(l).equals(level)) {
return l;
}
}
return null;
}
}
/**
* associate (create) level bits for the debugger
*
* @param d
* @param level
*/
public void associateDebugLevel(Class<? extends Debugger> d, Level level) {
Level lvl = Level.NoDebug;
if (!instancesClass.containsKey(d.getName())) {
lvl = new Level(bits._newBit(level.n));
} else {
lvl = bitMap(d);
if (lvl.and(level).equals(Level.NoDebug)) { /*
* find a new bit if not present
*/
Level newLevel = findVacantLevelBits(level);
lvl = lvl.or(newLevel != null ? newLevel : new Level(bits._newBit(level.n)));
}
}
if (isDebugLevelEnabled(_VOID)) {
System.out.println("associated " + d.getSimpleName() + " with debuglevel " + lvl.toString());
}
instancesClass.put(d.getName(), lvl);
instances.put(d.getName(), new SoftDebugger(d));
}
/**
* @param d
*/
public void deassociateJXADebugSys(Class<? extends Debugger> d) {
deassociateDebugLevel(d, jxaDebugSys);
}
/**
* @param d
*/
public void deassociateJXADebug(Class<? extends Debugger> d) {
deassociateDebugLevel(d, jxaDebug);
}
/**
* deassociate (remove) level bits for the debugger
*
* @param d
* @param level
*/
public void deassociateDebugLevel(Class<? extends Debugger> d, Level level) {
Level lvl = bitMap(d);
Level lvlFree = levelBits(d, level);
availableBits.add(lvlFree);
instancesClass.put(d.getName(), lvl.andNot(lvlFree));
instances.put(d.getName(), new SoftDebugger(d));
if (isDebugLevelEnabled(_VOID)) {
System.out.println("unassociated " + d.getSimpleName() + " with debuglevel " + lvl.toString());
}
}
/**
* TICK DEBUG LINES THAT DON'T DISPLAY EVRY FRAME
*/
/**
* ticktime
*/
private long ticker = 0l, ticktime = 1000l;
/**
*
* @return current tick time (delay between each tick)
*/
public long getTickTime() {
return ticktime;
}
public void setTickTime(long delay) {
if (delay < 0) {
throw new JXAException("no negative value please");
}
ticktime = delay;
}
private boolean print(PrintStream out, boolean newLine, String message, boolean tick) {
long c = System.currentTimeMillis();
boolean printed = c - ticker > ticktime;
if (printed) {
if (tick) {
ticker = c;
}
if (newLine) {
out.println(message);
} else {
out.print(message);
}
}
return printed;
}
/**
* @param out
* @return true if it printed something according to the tickTime delay set.
* @see #setTickTime(long)
* @see #getTickTime(long)
* @param tick set true to perform a tick (and bypass next message until it reaches the tick time)
*/
public boolean tickPrintln(PrintStream out, String message, boolean tick) {
return print(out, true, message, tick);
}
/**
* @param out
* @return true if it printed something according to the tickTime delay set.
* @see #setTickTime(long)
* @see #getTickTime(long)
* @param tick set true to perform a tick (and bypass next message until it reaches the tick time)
*/
public boolean tickPrint(PrintStream out, String message, boolean tick) {
return print(out, false, message, tick);
}
}