content: package edu.berkeley.nlp.lm.util;
  
  import java.io.FileNotFoundException;
  import java.io.FileOutputStream;
  import java.io.PrintStream;
  import java.util.Stack;
  
  /**
   * Basic logging singleton class. The underlying LogInterface instance can be
   * changed to customize logging behaviour by calling setGlobalLogger.
   * 
   * @author adampauls
   * 
   */
  @SuppressWarnings("ucd")
  public class Logger
  {
  
  	/**
  	 * Logging interface.
  	 * 
  	 * @author adampauls
  	 * 
  	 */
  	public static interface LogInterface
  	{
  		/**
  		 * Log a string, but only once in a while. This is useful when we are
  		 * logging frequently and only wish to see lines every second or so. How
  		 * often logging is done is up to the implementation.
  		 * 
  		 * @param s
  		 *            printf style string
  		 * @param args
  		 *            printf args
  		 */
  		public void logs(String s, Object... args);
  
  		/**
  		 * Always log this string.
  		 * 
  		 * @param s
  		 */
  		public void logss(String s);
  
  		public void logss(String string, Object... args);
  
  		/**
  		 * Start a track (a function, or some other logical unit of computation)
  		 * with a name given by <code>s</code>.
  		 * 
  		 * @param s
  		 */
  		public void startTrack(String s);
  
  		/**
  		 * Ends a track, printing out how long the track took.
  		 */
  		public void endTrack();
  
  		public void dbg(String s);
  
  		public void err(String s);
  
  		public void err(String s, Object... args);
  
  		public void warn(String s);
  
  		public void warn(String string, Object... args);
  
  	}
  
  	/**
  	 * Logs to System.out and System.err
  	 * 
  	 * @author Aria Haghighi
  	 * 
  	 */
  	@SuppressWarnings("ucd")
  	public static class SystemLogger implements LogInterface
  	{
  
  		private final PrintStream out;
  
  		private final PrintStream err;
  
  		private int trackLevel = 0;
  
  		private final boolean debug = true;
  
  		public SystemLogger(final PrintStream out, final PrintStream err) {
  			this.out = out;
  			this.err = err;
  		}
  
  		public void close() {
  			if (out != null) {
  				out.close();
  			}
  			if (err != null) {
  				err.close();
  			}
  		}
  
  		public SystemLogger(final String outFile, final String errFile) throws FileNotFoundException {
  			this(outFile != null ? new PrintStream(new FileOutputStream(outFile)) : null, errFile != null ? new PrintStream(new FileOutputStream(errFile))
  				: null);
  		}
  
  		public SystemLogger() {
  			this(System.out, System.err);
  		}
  
  		private final Stack<Long> trackStartTimes = new Stack<Long>();
  
  		private String getIndentPrefix() {
  			final StringBuilder builder = new StringBuilder();
  			for (int i = 0; i < trackLevel; ++i) {
  				builder.append("\t");
  			}
  			return builder.toString();
  		}
  
  		private void output(final PrintStream o, final String txt) {
  			if (o == null) return;
  			final String[] lines = txt.split("\n");
  			final String prefix = getIndentPrefix();
  			for (final String line : lines) {
  				o.println(prefix + line);
  			}
  		}
  
  		@Override
  		public void dbg(final String s) {
  			if (debug) output(out, "[dbg] " + s);
  		}
  
  		private String timeString(final double milliSecs_) {
  			double milliSecs = milliSecs_;
  			String timeStr = "";
  			final int hours = (int) (milliSecs / (1000 * 60 * 60));
  			if (hours > 0) {
  				milliSecs -= hours * 1000 * 60 * 60;
  				timeStr += hours + "h";
  			}
  			final int mins = (int) (milliSecs / (1000 * 60));
  			if (mins > 0) {
  				milliSecs -= mins * 1000.0 * 60.0;
  				timeStr += mins + "m";
  			}
  			final int secs = (int) (milliSecs / 1000.0);
  			//if (secs > 0) {
  			//milliSecs -= secs * 1000.0;
  			timeStr += secs + "s";
  			//}
  
  			return timeStr;
  		}
  
  		@Override
  		public void endTrack() {
  			String timeStr = null;
  			synchronized (this) {
  				trackLevel--;
  				final double milliSecs = System.currentTimeMillis() - trackStartTimes.pop();
  				timeStr = timeString(milliSecs);
  			}
  			output(out, "} " + (timeStr != null ? "[" + timeStr + "]" : ""));
  		}
  
  		@Override
  		public void err(final String s) {
  			err.println(s);
  		}
  
  		public void logs(final String s) {
  			output(out, s);
  		}
  
  		@Override
  		public void logss(final String s) {
  			output(out, s);
  		}
  
  		@Override
  		public void startTrack(final String s) {
  			output(out, s + " {");
  			synchronized (this) {
  				trackLevel++;
  				trackStartTimes.push(System.currentTimeMillis());
  			}
  		}
  
  		@Override
  		public void warn(final String s) {
  			output(err, "[warn] " + s);
  		}
  
  		@Override
  		public void logs(final String s, final Object... args) {
  			logs(String.format(s, args));
  		}
  
  		@Override
  		public void err(final String s, final Object... args) {
  			output(err, "[err] " + String.format(s, args));
  		}
  
  		@Override
  		public void warn(final String string, final Object... args) {
  			warn(String.format(string, args));
  		}
  
  		@Override
  		public void logss(final String string, final Object... args) {
  			logss(String.format(string, args));
  		}
  	}
  
  	/**
  	 * Default logging goes nowhere.
  	 * 
  	 * @author adampauls
  	 * 
  	 */
  	@SuppressWarnings("ucd")
  	public static class NullLogger implements LogInterface
  	{
  
  		@Override
  		public void logs(final String s, final Object... args) {
  		}
  
  		@Override
  		public void logss(final String s) {
  		}
  
  		@Override
  		public void startTrack(final String s) {
  		}
  
  		@Override
  		public void endTrack() {
  		}
  
  		@Override
  		public void dbg(final String s) {
  		}
  
  		@Override
  		public void err(final String s) {
  		}
  
  		@Override
  		public void err(final String s, final Object... args) {
  		}
  
  		@Override
  		public void warn(final String s) {
  		}
  
  		@Override
  		public void warn(final String string, final Object... args) {
  		}
  
  		@Override
  		public void logss(final String string, final Object... args) {
  		}
  
  	}
  
  	/**
  	 * Convenience class for stringing together loggers.
  	 * 
  	 * @author adampauls
  	 * 
  	 * 
  	 */
  	@SuppressWarnings("ucd")
  	public static class CompoundLogger implements LogInterface
  	{
  		private final LogInterface[] loggers;
  
  		public CompoundLogger(final LogInterface... loggers) {
  			this.loggers = loggers;
  		}
  
  		@Override
  		public void logs(final String s, final Object... args) {
  			for (final LogInterface logger : loggers) {
  				logger.logs(s, args);
  			}
  		}
  
  		@Override
  		public void logss(final String s) {
  			for (final LogInterface logger : loggers) {
  				logger.logss(s);
  			}
  		}
  
  		@Override
  		public void startTrack(final String s) {
  			for (final LogInterface logger : loggers) {
  				logger.startTrack(s);
  			}
  		}
  
  		@Override
  		public void endTrack() {
  			for (final LogInterface logger : loggers) {
  				logger.endTrack();
  			}
  		}
  
  		@Override
  		public void dbg(final String s) {
  			for (final LogInterface logger : loggers) {
  				logger.dbg(s);
  			}
  		}
  
  		@Override
  		public void err(final String s) {
  			for (final LogInterface logger : loggers) {
  				logger.err(s);
  			}
  		}
  
  		@Override
  		public void err(final String s, final Object... args) {
  			for (final LogInterface logger : loggers) {
  				logger.err(s, args);
  			}
  		}
  
  		@Override
  		public void warn(final String s) {
  			for (final LogInterface logger : loggers) {
  				logger.warn(s);
  			}
  		}
  
  		@Override
  		public void warn(final String string, final Object... args) {
  			for (final LogInterface logger : loggers) {
  				logger.warn(string, args);
  			}
  		}
  
  		@Override
  		public void logss(final String string, final Object... args) {
  			for (final LogInterface logger : loggers) {
  				logger.logss(string, args);
  			}
  		}
  	}
  
  	public synchronized static void setGlobalLogger(final LogInterface logger) {
  		instance = logger;
  	}
  
  	public synchronized static LogInterface getGlobalLogger() {
  		return instance;
  	}
  
  	private static LogInterface instance = new NullLogger();
  
  	public static LogInterface i() {
  		return instance;
  	}
  
  	public static void setLogger(final LogInterface i) {
  		instance = i;
  	}
  
  	public static void logs(final String s) {
  		i().logs(s);
  	}
  
  	// Static Logger Methods
  	public static void logs(final String s, final Object... args) {
  		i().logs(s, args);
  	}
  
  	public static void logss(final String s) {
  		i().logss(s);
  	}
  
  	public static void startTrack(final String s, final Object... args) {
  		i().startTrack(String.format(s, args));
  	}
  
  	public static void endTrack() {
  		i().endTrack();
  	}
  
  	public static void dbg(final String s) {
  		i().dbg(s);
  	}
  
  	public static void err(final String s) {
  		i().err(s);
  	}
  
  	public static void err(final String s, final Object... args) {
  		i().err(s, args);
  	}
  
  	public static void warn(final String s) {
  		i().warn(s);
  	}
  
  	public static void warn(final String string, final Object... args) {
  		i().warn(string, args);
  	}
  
  	public static void logss(final String string, final Object... args) {
  		i().logss(string, args);
  	}
  
  }
  