Commit in lcsim/src/org/lcsim/contrib/onoprien/util/job on MAIN
Driver.java+189added 1.1
JobManager.java+192-671.1 -> 1.2
+381-67
1 added + 1 modified, total 2 files
JobManager: JobEventListener sequencing, singleton access

lcsim/src/org/lcsim/contrib/onoprien/util/job
Driver.java added at 1.1
diff -N Driver.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Driver.java	2 Dec 2008 23:56:10 -0000	1.1
@@ -0,0 +1,189 @@
+package org.lcsim.contrib.onoprien.util.job;
+
+import java.util.logging.Level;
+import org.lcsim.contrib.onoprien.util.*;
+
+import org.lcsim.event.EventHeader;
+
+/**
+ * Base class for drivers. Adds functionality to {@link org.lcsim.util.Driver}.
+ * <p>
+ * Logging facility included in this class simply prints messages to standard output
+ * when the {@link #log log(String, Level)} method is called with the level greater 
+ * or equal to the logging level set for this driver.
+ * <p>
+ * Typically, logging levels are set according to the following guidelines:
+ * <dl>
+ * <dt>SEVERE</dt><dd>Error messages.</dd>
+ * <dt>WARNING</dt><dd>Messages about suspected errors.</dd>
+ * <dt>INFO (default)</dt><dd>Very little output - things the user would like to see
+ *         when running interactively but not debugging or testing.</dd>
+ * <dt>CONFIG</dt><dd>In addition, print out configuration information.</dd>
+ * <dt>FINE</dt><dd>Print most important statistics for each major step in event processing.</dd>
+ * <dt>FINER</dt><dd>Output for debugging</dd>
+ * <dt>FINEST</dt><dd>Detailed diagnostic output - something only the developer would
+ *         like to see, and only when debugging a particular class or method.</dd>
+ * </dl>
+ * 
+ * 
+ * @author D. Onoprienko
+ * @version $Id: Driver.java,v 1.1 2008/12/02 23:56:10 onoprien Exp $
+ */
+public class Driver extends org.lcsim.util.Driver {
+  
+// -- Constructors :  ----------------------------------------------------------
+  
+  public Driver() {
+    _jMan = JobManager.defaultInstance();
+  }
+  
+// -- Setters :  ---------------------------------------------------------------
+
+  /**
+   * Set any parameter. 
+   * The following parameters can be set with this method:
+   * <p><dl>
+   * <dt>"LOG_LEVEL"</dt> <dd>Logging level. See {@link Level} for possible values. 
+   *             <br>Default: INFO.</dd>
+   * <dt>"LOG_DEFAULT_LEVEL"</dt> <dd>Default logging level. Calling {@link #log(String)}
+   *             will log at this level. This is intended to be set only in the driver's
+   *             own constructor - if a particular driver logs many messages at a particular level,
+   *             the code can be made more concise by making it the default level.
+   *             <br>Default: INFO.</dd>
+   * <dt>"LOG_LEVEL_GLOBAL"</dt> <dd>Logging level for this driver and all its known sub-drivers. 
+   *             <br>Default: INFO.</dd>
+   * <dt>"LOG_PREFIX"</dt> <dd>Logging prefix.
+   *             <br>Default: <tt>null</tt> (driver name will be used as a prefix).</dd></dl>
+   * 
+   * @param name   Name of parameter to be set. Case is ignored.
+   * @param values  List of values to be used for setting the parameter.
+   * @throws NoSuchParameterException Thrown if the supplied parameter name is unknown.
+   * @throws IllegalArgumentException Thrown if incorrect number of values, or a value
+   *                                  of incorrect type is supplied.
+   */
+  public void set(String name, Object... values) {
+    Object value = values.length == 0 ? null : values[0];
+    try {
+      if (name.equalsIgnoreCase("LOG_LEVEL")) {
+        try {
+          _logLevel = ((Level)value).intValue();
+        } catch (ClassCastException xx) {
+          try {
+            _logLevel = Level.parse((String)value).intValue();
+          } catch (IllegalArgumentException xxx) {
+            throw new IllegalArgumentException("Unknown logging level: "+(String)value);
+          }
+        }
+      } else if (name.equalsIgnoreCase("LOG_DEFAULT_LEVEL")) {
+        try {
+          _defLevel = ((Level)value);
+        } catch (ClassCastException xx) {
+          try {
+            _defLevel = Level.parse((String)value);
+          } catch (IllegalArgumentException xxx) {
+            throw new IllegalArgumentException("Unknown logging level: "+(String)value);
+          }
+        }
+      } else if (name.equalsIgnoreCase("LOG_LEVEL_GLOBAL")) {
+        try {
+          _logLevel = ((Level)value).intValue();
+        } catch (ClassCastException xx) {
+          try {
+            _logLevel = Level.parse((String)value).intValue();
+          } catch (IllegalArgumentException xxx) {
+            throw new IllegalArgumentException("Unknown logging level: "+(String)value);
+          }
+        }
+        for (org.lcsim.util.Driver daughter : drivers()) {
+          try {
+            ((Driver)daughter).set(name, value);
+          } catch (ClassCastException xx) {}
+        }
+      } else if (name.equalsIgnoreCase("LOG_PREFIX")) {
+        if (value == null) {
+          _logPrefix = getName() + " : ";
+        } else {
+          _logPrefix = (String) value;
+          if (_logPrefix.length() == 0) _logPrefix = null;
+        }
+      } else {
+        throw new NoSuchParameterException(name, this.getClass());
+      }
+    } catch (ClassCastException x) {
+      throw new IllegalArgumentException(ERR_VIT, x);
+    }
+  }
+  
+// -- Logging :  ---------------------------------------------------------------
+  
+  /** Log a message at the given level. */
+  public void log(String message, Level level) {
+    if (level.intValue() >= _logLevel) {
+      if (_logPrefix != null) message = _logPrefix + message;
+      System.out.println(message);
+    }
+  }
+  
+  /** 
+   * Log a message at the level set by a call to <tt>set("LOG_DEFAULT_LEVEL", level)</tt>.
+   */
+  public void log(String message) {
+    log(message, _defLevel);
+  }
+  
+  /** 
+   * Returns <tt>true</tt> if messages at the given level are being printed out.
+   * That is, when the current logging level set for this driver is at or below
+   * the given <tt>Level</tt>.
+   */
+  public boolean isLogging(Level level) {
+    return level.intValue() >= _logLevel;
+  }
+  
+  /**
+   * Add a driver that will log the message at the given level.
+   */
+  public void addLog(final String message, final Level level) {
+    add(new Driver() {
+      public void process(EventHeader event) {
+        Driver.this.log(message, level);
+      }
+    });
+  }
+  
+  /**
+   * Add a driver that will log the message at the level set by a call to <tt>set("LOG_DEFAULT_LEVEL", level)</tt>.
+   */
+  public void addLog(String message) {
+    addLog(message, _defLevel);
+  }
+  
+// -- Adding JobManager :  -----------------------------------------------------
+  
+  public void add(org.lcsim.util.Driver driver) {
+    if (driver instanceof Driver) {
+      ((Driver)driver)._jMan = null;
+    }
+    super.add(driver);
+  }
+
+  public void process(EventHeader event) {
+    if (_jMan != null) _jMan.process(event);
+    super.process(event);
+  }
+  
+// -- Private parts :  ---------------------------------------------------------
+  
+  /** Setter error message: "Value of incompatible type " */
+  static public final String ERR_VIT = "Value of incompatible type ";
+  /** Setter error message: "Illegal number of values for name " */
+  static public final String ERR_INV = "Illegal number of values for name ";
+  /** Setter error message: "Illegal value for name " */
+  static public final String ERR_IV = "Illegal value for name ";
+  
+  private int _logLevel = Level.INFO.intValue();
+  private Level _defLevel = Level.INFO;
+  private String _logPrefix = getName() + " : ";
+  
+  private JobManager _jMan;
+}

lcsim/src/org/lcsim/contrib/onoprien/util/job
JobManager.java 1.1 -> 1.2
diff -u -r1.1 -r1.2
--- JobManager.java	25 Nov 2008 21:02:58 -0000	1.1
+++ JobManager.java	2 Dec 2008 23:56:10 -0000	1.2
@@ -1,39 +1,58 @@
 package org.lcsim.contrib.onoprien.util.job;
 
-import java.lang.ref.WeakReference;
 import java.util.*;
 
 import org.lcsim.conditions.ConditionsListener;
 import org.lcsim.conditions.ConditionsEvent;
 import org.lcsim.conditions.ConditionsManager;
 import org.lcsim.conditions.ConditionsManager.ConditionsSetNotFoundException;
-import org.lcsim.contrib.onoprien.crux.geom.CalGeometry;
-import org.lcsim.event.CalorimeterHit;
 import org.lcsim.event.EventHeader;
 import org.lcsim.geometry.Detector;
 import org.lcsim.util.aida.AIDA;
 
-import org.lcsim.contrib.onoprien.util.Driver;
-
 /**
  * Driver that provides miscellaneous services to other classes.
  * <p>
  * Current functionality:
  * <ul>
+ *
  * <li>Accepts listener registration and dispatch events that trigger geometry dependent 
- *     initialization in client classes.
+ *     initialization in client classes. Listeners can be registered along with 
+ *     dependencies among them - <tt>JobMamager</tt> guaranties that the listeners
+ *     specified as "prerequisites" for come other listener will receive the event first.
+ *
+ * <li>Allows registration and retrieval of singleton objects of any type.
+ *
  * <li>Provides access to the default AIDA object that can be used for histogramming, plotting, etc.
  * </ul>
  * 
  * @author D. Onoprienko
- * @version $Id: JobManager.java,v 1.1 2008/11/25 21:02:58 onoprien Exp $
+ * @version $Id: JobManager.java,v 1.2 2008/12/02 23:56:10 onoprien Exp $
  */
-public class JobManager extends Driver implements ConditionsListener {
+public class JobManager extends org.lcsim.util.Driver implements ConditionsListener {
+  
+// -- Private parts :  ---------------------------------------------------------
+  
+  static private JobManager _defInstance;
+  
+  private JobEvent _lastEvent;
+  private ArrayList<Node> _listeners;
+  
+  private HashMap<Class, Object> _singletons;
+  
+  private AIDA _aida;  
+
   
 // -- Constructors and initialization :  ---------------------------------------
   
   private JobManager() {
+    
+    _listeners = new ArrayList<Node>(1);
+    
+    _singletons = new HashMap<Class, Object>();
+    
     _aida = AIDA.defaultInstance();
+    
     getConditionsManager().addConditionsListener(this);
   }
   
@@ -43,6 +62,18 @@
    */
   private void detectorChanged(JobEvent jobEvent) {
   }
+
+  
+// -- Event processing :  ------------------------------------------------------
+  
+  /** Called by the framework to process event. */
+  public void process(EventHeader event) {
+    if (_lastEvent == null) {
+      _lastEvent = new JobEvent(this, event.getDetector());
+      fireJobEvent(_lastEvent);
+    }
+  }
+
   
 // -- Getters :  ---------------------------------------------------------------
   
@@ -60,22 +91,28 @@
   }
 
   
-// -- Event processing :  ------------------------------------------------------
+// -- Registering/retrieving singletons :  -------------------------------------
   
-  /** Called by the framework to process event. */
-  public void process(EventHeader event) {
-    
-    // Let the listeners know they have to re-validate themselves
-    
-    if (_lastEvent == null) {
-      _lastEvent = new JobEvent(this, event.getDetector());
-      fireJobEvent(_lastEvent);
-    }
-    
-    // Run daughter Drivers :
-    
-    super.process(event);
+  /** 
+   * Retrieve a singleton of the specified type.
+   * If a singleton of the given type has not been registered with this <tt>JobManager</tt>,
+   * <tt>IllegalArgumentException</tt> is thrown.
+   */
+  public <T> T get(Class<T> singletonType) {
+    T singleton = (T) _singletons.get(singletonType);
+    if (singleton == null) throw new IllegalArgumentException("Singleton of this type is not registered");
+    return singleton;
+  }
+
+  /**
+   * Register a singleton of the specified type.
+   * If the given object is not an instance of the specified type, <tt>IllegalArgumentException</tt> is thrown.
+   */
+  public void put(Object singleton, Class type) {
+    if (! type.isInstance(singleton)) throw new IllegalArgumentException();
+    _singletons.put(type, singleton);
   }
+
   
 // -- Handling of event listeners :  -------------------------------------------
 
@@ -89,9 +126,8 @@
   private void fireJobEvent(JobEvent jEvent) {
     detectorChanged(jEvent);
     ArrayList<JobEventListener> listeners = new ArrayList<JobEventListener>(_listeners.size());
-    for (WeakReference<JobEventListener> ref : _listeners) {
-      JobEventListener listener = ref.get();
-      if (listener != null) listeners.add(listener);
+    for (Node node : _listeners) {
+      listeners.add(node.listener);
     }
     for (JobEventListener listener : listeners) {
       listener.detectorChanged(jEvent);
@@ -103,61 +139,150 @@
    * <p>
    * If the <tt>JobManager</tt> has valid detector information when a new listener is
    * added, <tt>JobEvent</tt> is dispatched to that listener immediately.
-   * <p>
-   * Listeners are kept through weak references, so being registered with 
-   * JobManager as a listener does not prevent objects from being garbage-collected.
    *
    * @param  listener       Listener to be added.
    * @param  prerequisite   Zero or more listeners that should receive the event before the listener
-   *                        specified by the first parameter. Prerequisite listeners that have not been
-   *                        registered earlier will be added now (in order in which they are listed).
-   *                        If the listener specified by the first parameter is already registered, 
-   *                        neither it nor its prerequisite will be added. 
+   *                        specified by the first parameter, in arbitrary order. Prerequisite 
+   *                        listeners that have not been registered earlier will be added now.
+   * @throws  IllegalArgumentException - if the specified list of prerequisites is inconsistent with 
+   *                                    previously registered dependencies between listeners (introduces
+   *                                    cyclic dependencies).
    */
   public void addListener(JobEventListener listener, JobEventListener... prerequisite) {
-    
-    LinkedList<JobEventListener> prereq = new LinkedList<JobEventListener>(Arrays.asList(prerequisite));
-    
-    Iterator<WeakReference<JobEventListener>> it = _listeners.iterator();
-    boolean newListener = true;
-    while (it.hasNext()) {
-      JobEventListener lstnr = it.next().get();
-      if (lstnr == null) {
-        it.remove();
-      } else if (lstnr == listener) {
-        newListener = false;
-      } else {
-        prereq.remove(lstnr);
+    if (listener == null) throw new IllegalArgumentException();
+    Node newNode = new Node(listener);
+    if (prerequisite.length == 0) {
+      for (Node node : _listeners) {
+        if (node.listener == listener) return;
       }
-    }
-    if (newListener) {
-      for (JobEventListener preListener : prereq) {
-        _listeners.add(new WeakReference<JobEventListener>(preListener));
-        if (_lastEvent != null) preListener.detectorChanged(_lastEvent);
+      _listeners.add(0, newNode);
+    } else {
+      LinkedHashSet<JobEventListener> prereq = new LinkedHashSet<JobEventListener>(Arrays.asList(prerequisite));
+      if (prereq.contains(listener)) throw new IllegalArgumentException();
+      newNode.parents = new ArrayList<Node>(prereq.size());
+      Node oldNode = null;
+      for (Node node : _listeners) {
+        JobEventListener lstnr = node.listener;
+        boolean old = prereq.remove(lstnr);
+        if (old) {
+          newNode.parents.add(node);
+        } else  if (lstnr == listener)  {
+          oldNode = node;
+        }
+      }
+      if (!prereq.isEmpty()) {
+        for (JobEventListener lstnr : prereq) {
+          if (_lastEvent != null) lstnr.detectorChanged(_lastEvent);
+          Node node = new Node(lstnr);
+          _listeners.add(0, node);
+          newNode.parents.add(node);
+        }
+      }
+      if (oldNode == null) {
+        if (_lastEvent != null) listener.detectorChanged(_lastEvent);
+        _listeners.add(newNode);
+        for (Node node : newNode.parents) {
+          if (node.daughters == null) node.daughters = new ArrayList<Node>(1);
+          node.daughters.add(newNode);
+          if (node.depth >= newNode.depth) newNode.depth = node.depth + 1;
+        }
+      } else {
+        if (oldNode.parents == null) oldNode.parents = new ArrayList<Node>(newNode.parents);
+        HashSet<Node> newParents = new HashSet<Node>(newNode.parents);
+        newParents.removeAll(oldNode.parents);
+        oldNode.parents.addAll(newParents);
+        for (Node node : newParents) {
+          if (node.daughters == null) node.daughters = new ArrayList<Node>(1);
+          node.daughters.add(oldNode);
+        }
+        oldNode.setDepth();
       }
-      _listeners.add(new WeakReference<JobEventListener>(listener));
-      if (_lastEvent != null) listener.detectorChanged(_lastEvent);
+      Collections.sort(_listeners);
     }
-    _listeners.trimToSize();
   }
   
-  /** Remove a listener. */
-  public void removeListener(JobEventListener listener) {
-    Iterator<WeakReference<JobEventListener>> it = _listeners.iterator();
+  /**
+   * Remove a listener.
+   * Throws <tt>IllegalArgumentException</tt> if the specified listener is a 
+   * prerequisite for other registered listeners.
+   *
+   * @return <tt>true</tt> if the list of listeners changed as a result of the call.
+   */
+  public boolean removeListener(JobEventListener listener) {
+    ListIterator<Node> it = _listeners.listIterator();
     while (it.hasNext()) {
-      JobEventListener lstnr = it.next().get();
-      if (lstnr == null || lstnr == listener) {
-        it.remove();
+      Node node = it.next();
+      if (node.listener == listener) {
+        if (node.daughters == null || node.daughters.isEmpty()) {
+          if (node.parents != null) {
+            for (Node p : node.parents) {
+              p.daughters.remove(node);
+              if (p.daughters.isEmpty()) p.daughters = null;
+            }
+          }
+          it.remove();
+          return true;
+        } else {
+          throw new IllegalArgumentException();
+        }
       }
     }
+    return false;
   }
   
-// -- Private parts :  ---------------------------------------------------------
-  
-  static private JobManager _defInstance;
-  
-  private JobEvent _lastEvent;
-  private ArrayList<WeakReference<JobEventListener>> _listeners = new ArrayList<WeakReference<JobEventListener>>(1);
-  
-  private AIDA _aida;  
+  /**
+   * Replaces a listener while keeping its dependencies.
+   * Throws <tt>IllegalArgumentException</tt> if any of the specified listeners is <tt>null</tt>.
+   *
+   * @return <tt>true</tt> if the list of listeners changed as a result of the call.
+   */
+  public boolean replaceListener(JobEventListener oldListener, JobEventListener newListener) {
+    if (oldListener == null || newListener == null) throw new IllegalArgumentException();
+    if (oldListener == newListener) return false;
+    boolean changed = false;
+    for (Node node : _listeners) {
+      if (node.listener == oldListener) {
+        node.listener = newListener;
+        changed = true;
+      } else if (node.listener == newListener) {
+        throw new IllegalArgumentException();
+      }
+    }
+    return changed;
+  }
+
+  /** Node in the collection of JobEventListeners. */
+  private class Node implements Comparable<Node> {
+    JobEventListener listener;
+    ArrayList<Node> parents;
+    ArrayList<Node> daughters;
+    int depth;
+    Node(JobEventListener listener) {this.listener = listener;}
+    public int compareTo(Node that) {return depth - that.depth;}
+    void setDepth() {
+      depth = calculateDepth(this, false);
+      adjustDaughters(this);
+    }
+    int calculateDepth(Node node, boolean check) {
+      if (check && node == this) throw new IllegalArgumentException();
+      if (node.parents == null) return 0;
+      int dep = 0;
+      for (Node n : node.parents) {
+        int pdep = calculateDepth(n, true);
+        if (pdep >= dep) dep = pdep + 1;
+      }
+      return dep;
+    }
+    void adjustDaughters(Node node) {
+      if (node.daughters == null) return;
+      for (Node d : node.daughters) {
+        if (d.depth > node.depth) {
+          continue;
+        } else {
+          d.depth = node.depth + 1;
+          adjustDaughters(d);
+        }
+      }
+    }
+  }
 }
CVSspam 0.2.8