001 package net.sf.cpsolver.ifs.util; 002 003 import java.io.Serializable; 004 import java.text.SimpleDateFormat; 005 import java.util.*; 006 import org.dom4j.Element; 007 008 /** 009 * Progress bar. 010 * <br><br> 011 * Single instance class for recording the current state. It also allows recursive storing/restoring of the progress. 012 * 013 * <br><br> 014 * Use:<ul><code> 015 * Progress.getInstance().setStatus("Loading input data");<br> 016 * Progress.getInstance().setPhase("Creating variables ...", nrVariables);<br> 017 * for (int i=0;i<nrVariables;i++) {<br> 018 * //load variable here<br> 019 * Progress.getInstance().incProgress();<br> 020 * }<br> 021 * Progress.getInstance().setPhase("Creating constraints ...", nrConstraints);<br> 022 * for (int i=0;i<nrConstraints;i++) {<br> 023 * //load constraint here<br> 024 * Progress.getInstance().incProgress();<br> 025 * }<br> 026 * Progress.getInstance().setStatus("Solving problem");<br> 027 * ...<br> 028 * </code></ul> 029 * 030 * @version 031 * IFS 1.1 (Iterative Forward Search)<br> 032 * Copyright (C) 2006 Tomáš Müller<br> 033 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 034 * Lazenska 391, 76314 Zlin, Czech Republic<br> 035 * <br> 036 * This library is free software; you can redistribute it and/or 037 * modify it under the terms of the GNU Lesser General Public 038 * License as published by the Free Software Foundation; either 039 * version 2.1 of the License, or (at your option) any later version. 040 * <br><br> 041 * This library is distributed in the hope that it will be useful, 042 * but WITHOUT ANY WARRANTY; without even the implied warranty of 043 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 044 * Lesser General Public License for more details. 045 * <br><br> 046 * You should have received a copy of the GNU Lesser General Public 047 * License along with this library; if not, write to the Free Software 048 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 049 */ 050 public class Progress { 051 public static boolean sTraceEnabled = false; 052 private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(Progress.class); 053 public static SimpleDateFormat sDF = new SimpleDateFormat("MM/dd/yy HH:mm:ss.SSS"); 054 public static final int MSGLEVEL_TRACE = 0; 055 public static final int MSGLEVEL_DEBUG = 1; 056 public static final int MSGLEVEL_PROGRESS = 2; 057 public static final int MSGLEVEL_INFO = 3; 058 public static final int MSGLEVEL_STAGE = 4; 059 public static final int MSGLEVEL_WARN = 5; 060 public static final int MSGLEVEL_ERROR = 6; 061 public static final int MSGLEVEL_FATAL = 7; 062 063 private String iStatus=""; 064 private String iPhase=""; 065 private long iProgressMax=0; 066 private long iProgressCurrent=0; 067 private Vector iListeners = new FastVector(5); 068 private Vector iSave = new FastVector(5); 069 private Vector iLog = new FastVector(1000,1000); 070 071 private static Hashtable sInstances = new Hashtable(); 072 073 private Progress() {} 074 075 /** Progress default instance */ 076 public static Progress getInstance() { 077 return getInstance("--DEFAULT--"); 078 } 079 /** Progress instance */ 080 public static Progress getInstance(Object key) { 081 Progress progress = (Progress)sInstances.get(key); 082 if (progress==null) { 083 progress = new Progress(); 084 sInstances.put(key, progress); 085 } 086 return progress; 087 } 088 089 /** Change progress instance for the given key */ 090 public static void changeInstance(Object oldKey, Object newKey) { 091 removeInstance(newKey); 092 Progress progress = (Progress)sInstances.get(oldKey); 093 if (progress!=null) { 094 sInstances.remove(oldKey); 095 sInstances.put(newKey, progress); 096 } 097 } 098 099 /** Remove progress instance for the given key */ 100 public static void removeInstance(Object key) { 101 Progress progress = (Progress)sInstances.get(key); 102 if (progress!=null) { 103 progress.iListeners.clear(); 104 sInstances.remove(key); 105 } 106 } 107 108 /** Current status */ 109 public String getStatus() { return iStatus; } 110 /** Sets current status */ 111 public void setStatus(String status) { 112 message(MSGLEVEL_STAGE, status); 113 if (!status.equals(iStatus)) { 114 iPhase = ""; iProgressMax=0; iProgressCurrent=0; 115 iStatus = status; fireStatusChanged(); 116 } 117 } 118 /** Current phase */ 119 public String getPhase() { return iPhase; } 120 /** Sets current phase 121 * @param phase phase name 122 * @param progressMax maximum of progress bar 123 */ 124 public void setPhase(String phase, long progressMax) { 125 if (iSave.isEmpty() && !phase.equals(iPhase)) message(MSGLEVEL_PROGRESS,phase); 126 iPhase = phase; iProgressMax=progressMax; iProgressCurrent=0; firePhaseChanged(); 127 } 128 /** Sets current phase. Maximum of progress bar is set to 100. 129 * @param phase phase name 130 */ 131 public void setPhase(String phase) { setPhase(phase, 100); } 132 /** Update progress bar. 133 * @param progress progress between 0 and progressMax 134 */ 135 public void setProgress(long progress) { 136 if (iProgressCurrent!=progress) { 137 iProgressCurrent = progress; fireProgressChanged(); 138 } 139 } 140 /** Current progress */ 141 public long getProgress() { return iProgressCurrent; } 142 /** Maximum of current progress */ 143 public long getProgressMax() { return iProgressMax; } 144 /** Increment current progress */ 145 public void incProgress() { 146 iProgressCurrent++; 147 fireProgressChanged(); 148 } 149 /** Adds progress listener */ 150 public void addProgressListener(ProgressListener listener) { iListeners.addElement(listener); } 151 /** Remove progress listener */ 152 public void removeProgressListener(ProgressListener listener) { iListeners.removeElement(listener); } 153 /** Remove all progress listeners */ 154 public void clearProgressListeners() { iListeners.clear(); } 155 156 /** Save current progress to the heap memory */ 157 public synchronized void save() { 158 iSave.addElement(new Object[] { iStatus, iPhase, new Long(iProgressMax), new Long(iProgressCurrent) }); 159 fireProgressSaved(); 160 } 161 /** Resore the progress from the heap memory */ 162 public synchronized void restore() { 163 if (iSave.isEmpty()) return; 164 Object[] o = (Object[]) iSave.lastElement(); 165 iSave.removeElementAt(iSave.size()-1); 166 iStatus = (String)o[0]; 167 iPhase = (String)o[1]; 168 iProgressMax = ((Long)o[2]).longValue(); 169 iProgressCurrent = ((Long)o[3]).longValue(); 170 fireProgressRestored(); 171 } 172 /** Prints a message */ 173 public void message(int level, String message, Throwable t) { 174 Message m = new Message(level, message,t); 175 switch (level) { 176 case MSGLEVEL_TRACE : sLogger.debug(" -- "+message,t); break; 177 case MSGLEVEL_DEBUG : sLogger.debug(" -- "+message,t); break; 178 case MSGLEVEL_PROGRESS : sLogger.debug("["+message+"]",t); break; 179 case MSGLEVEL_INFO : sLogger.info(message,t); break; 180 case MSGLEVEL_STAGE : sLogger.info("["+message+"]",t); break; 181 case MSGLEVEL_WARN : sLogger.warn(message,t); break; 182 case MSGLEVEL_ERROR : sLogger.error(message,t); break; 183 case MSGLEVEL_FATAL : sLogger.fatal(message,t); break; 184 } 185 iLog.addElement(m); 186 fireMessagePrinted(m); 187 } 188 /** Prints a message */ 189 public void message(int level, String message) { 190 message(level, message,null); 191 } 192 /** Prints a trace message */ 193 public void trace(String message) { 194 if (!sTraceEnabled) return; 195 message(MSGLEVEL_TRACE, message); 196 } 197 /** Prints a debug message */ 198 public void debug(String message) { 199 message(MSGLEVEL_DEBUG, message); 200 } 201 /** Prints an info message */ 202 public void info(String message) { 203 message(MSGLEVEL_INFO, message); 204 } 205 /** Prints a warning message */ 206 public void warn(String message) { 207 message(MSGLEVEL_WARN, message); 208 } 209 /** Prints an error message */ 210 public void error(String message) { 211 message(MSGLEVEL_ERROR, message); 212 } 213 /** Prints a fatal message */ 214 public void fatal(String message) { 215 message(MSGLEVEL_FATAL, message); 216 } 217 /** Prints a trace message */ 218 public void trace(String message, Throwable e) { 219 if (!sTraceEnabled) return; 220 message(MSGLEVEL_TRACE, message, e); 221 } 222 /** Prints a debug message */ 223 public void debug(String message, Throwable e) { 224 message(MSGLEVEL_DEBUG, message, e); 225 } 226 /** Prints an info message */ 227 public void info(String message, Throwable e) { 228 message(MSGLEVEL_INFO, message, e); 229 } 230 /** Prints a warning message */ 231 public void warn(String message, Throwable e) { 232 message(MSGLEVEL_WARN, message, e); 233 } 234 /** Prints an error message */ 235 public void error(String message, Throwable e) { 236 message(MSGLEVEL_ERROR, message, e); 237 } 238 /** Prints a fatal message */ 239 public void fatal(String message, Throwable e) { 240 message(MSGLEVEL_FATAL, message, e); 241 } 242 /** Returns log (list of messages) */ 243 public Vector getLog() { return iLog; } 244 /** Returns log (list of messages). Only messages with the given level or higher are included. */ 245 public String getLog(int level) { 246 StringBuffer sb = new StringBuffer(); 247 for (Enumeration e=iLog.elements();e.hasMoreElements();) { 248 Message m = (Message)e.nextElement(); 249 String s = m.toString(level); 250 if (s!=null) sb.append(s+"\n"); 251 } 252 return sb.toString(); 253 } 254 /** Returns log in HTML format */ 255 public String getHtmlLog(int level, boolean includeDate) { 256 StringBuffer sb = new StringBuffer(); 257 for (Enumeration e=iLog.elements();e.hasMoreElements();) { 258 Message m = (Message)e.nextElement(); 259 String s = m.toHtmlString(level, includeDate); 260 if (s!=null) sb.append(s+"<br>"); 261 } 262 return sb.toString(); 263 } 264 /** Returns log in HTML format (only messages with the given level or higher are included) */ 265 public String getHtmlLog(int level, boolean includeDate, String fromStage) { 266 StringBuffer sb = new StringBuffer(); 267 for (Enumeration e=iLog.elements();e.hasMoreElements();) { 268 Message m = (Message)e.nextElement(); 269 if (m.getLevel()==MSGLEVEL_STAGE && m.getMessage().equals(fromStage)) 270 sb = new StringBuffer(); 271 String s = m.toHtmlString(level, includeDate); 272 if (s!=null) sb.append(s+"<br>"); 273 } 274 return sb.toString(); 275 } 276 /** Clear the log */ 277 public void clear() { 278 iLog.clear(); 279 } 280 281 private void fireStatusChanged() { 282 for (Enumeration e=iListeners.elements();e.hasMoreElements();) { 283 ProgressListener listener = (ProgressListener)e.nextElement(); 284 listener.statusChanged(iStatus); 285 } 286 } 287 private void firePhaseChanged() { 288 for (Enumeration e=iListeners.elements();e.hasMoreElements();) { 289 ProgressListener listener = (ProgressListener)e.nextElement(); 290 listener.phaseChanged(iPhase); 291 } 292 } 293 private void fireProgressChanged() { 294 for (Enumeration e=iListeners.elements();e.hasMoreElements();) { 295 ProgressListener listener = (ProgressListener)e.nextElement(); 296 listener.progressChanged(iProgressCurrent, iProgressMax); 297 } 298 } 299 private void fireProgressSaved() { 300 for (Enumeration e=iListeners.elements();e.hasMoreElements();) { 301 ProgressListener listener = (ProgressListener)e.nextElement(); 302 listener.progressSaved(); 303 } 304 } 305 private void fireProgressRestored() { 306 for (Enumeration e=iListeners.elements();e.hasMoreElements();) { 307 ProgressListener listener = (ProgressListener)e.nextElement(); 308 listener.progressRestored(); 309 } 310 } 311 312 private void fireMessagePrinted(Message message) { 313 for (Enumeration e=iListeners.elements();e.hasMoreElements();) { 314 ProgressListener listener = (ProgressListener)e.nextElement(); 315 listener.progressMessagePrinted(message); 316 } 317 } 318 319 /** Log nessage */ 320 public static class Message implements Serializable { 321 private static final long serialVersionUID = 1L; 322 private int iLevel = 0; 323 private String iMessage; 324 private Date iDate = null; 325 private String[] iStakTrace = null; 326 private Message(int level, String message, Throwable e) { 327 iLevel = level; iMessage = message; iDate = new Date(); 328 if (e!=null) { 329 StackTraceElement trace[] = e.getStackTrace(); 330 if (trace!=null) { 331 iStakTrace = new String[trace.length+1]; 332 iStakTrace[0]=e.getClass().getName()+": "+e.getMessage(); 333 for (int i=0;i<trace.length;i++) 334 iStakTrace[i+1]=trace[i].getClassName()+"."+trace[i].getMethodName()+(trace[i].getFileName()==null?"":"("+trace[i].getFileName()+(trace[i].getLineNumber()>=0?":"+trace[i].getLineNumber():"")+")"); 335 } 336 } 337 } 338 /** Creates message out of XML element */ 339 public Message(Element element) { 340 iLevel = Integer.parseInt(element.attributeValue("level","0")); 341 iMessage = element.attributeValue("msg"); 342 iDate = new Date(Long.parseLong(element.attributeValue("date","0"))); 343 List tr = element.elements("trace"); 344 if (tr!=null && !tr.isEmpty()) { 345 iStakTrace = new String[tr.size()]; 346 for (int i=0;i<tr.size();i++) 347 iStakTrace[i] = ((Element)tr.get(i)).getText(); 348 } 349 } 350 /** Message */ 351 public String getMessage() { return iMessage; } 352 /** Debug level */ 353 public int getLevel() { return iLevel; } 354 /** Time stamp */ 355 public Date getDate() { return iDate; } 356 /** Tracelog */ 357 private String getTraceLog() { 358 if (iStakTrace==null) return ""; 359 StringBuffer ret = new StringBuffer("\n"+iStakTrace[0]); 360 for (int i=1;i<iStakTrace.length;i++) 361 ret.append("\n at "+iStakTrace[i]); 362 return ret.toString(); 363 } 364 /** Tracelog as HTML */ 365 private String getHtmlTraceLog() { 366 if (iStakTrace==null) return ""; 367 StringBuffer ret = new StringBuffer("<BR>"+iStakTrace[0]); 368 for (int i=1;i<iStakTrace.length;i++) 369 ret.append("<BR> at "+iStakTrace[i]); 370 return ret.toString(); 371 } 372 /** String representation of the message (null if the message level is below the given level) */ 373 public String toString(int level) { 374 if (iLevel<level) return null; 375 switch (iLevel) { 376 case MSGLEVEL_TRACE : return sDF.format(iDate)+" -- "+iMessage+getTraceLog(); 377 case MSGLEVEL_DEBUG : return sDF.format(iDate)+" -- "+iMessage+getTraceLog(); 378 case MSGLEVEL_PROGRESS : return sDF.format(iDate)+" ["+iMessage+"]"+getTraceLog(); 379 case MSGLEVEL_INFO : return sDF.format(iDate)+" "+iMessage+getTraceLog(); 380 case MSGLEVEL_STAGE : return sDF.format(iDate)+" >>> "+iMessage+" <<<"+getTraceLog(); 381 case MSGLEVEL_WARN : return sDF.format(iDate)+" WARNING: "+iMessage+getTraceLog(); 382 case MSGLEVEL_ERROR : return sDF.format(iDate)+" ERROR: "+iMessage+getTraceLog(); 383 case MSGLEVEL_FATAL : return sDF.format(iDate)+" >>>FATAL: "+iMessage+" <<<"+getTraceLog(); 384 } 385 return null; 386 } 387 /** String representation of the message */ 388 public String toString() { return toString(MSGLEVEL_TRACE); } 389 /** HTML representation of the message */ 390 public String toHtmlString(int level, boolean includeDate) { 391 if (iLevel<level) return null; 392 switch (iLevel) { 393 case MSGLEVEL_TRACE : return (includeDate?sDF.format(iDate):"")+" -- "+iMessage+getHtmlTraceLog(); 394 case MSGLEVEL_DEBUG : return (includeDate?sDF.format(iDate):"")+" -- "+iMessage+getHtmlTraceLog(); 395 case MSGLEVEL_PROGRESS : return (includeDate?sDF.format(iDate):"")+" "+iMessage+getHtmlTraceLog(); 396 case MSGLEVEL_INFO : return (includeDate?sDF.format(iDate):"")+" "+iMessage+getHtmlTraceLog(); 397 case MSGLEVEL_STAGE : return "<br>"+(includeDate?sDF.format(iDate):"")+" <span style='font-weight:bold;'>"+iMessage+"</span>"+getHtmlTraceLog(); 398 case MSGLEVEL_WARN : return (includeDate?sDF.format(iDate):"")+" <span style='color:orange;font-weight:bold;'>WARNING:</span> "+iMessage+getHtmlTraceLog(); 399 case MSGLEVEL_ERROR : return (includeDate?sDF.format(iDate):"")+" <span style='color:red;font-weight:bold;'>ERROR:</span> "+iMessage+getHtmlTraceLog(); 400 case MSGLEVEL_FATAL : return (includeDate?sDF.format(iDate):"")+" <span style='color:red;font-weight:bold;'>>>>FATAL: "+iMessage+" <<<</span>"+getHtmlTraceLog(); 401 } 402 return null; 403 } 404 /** HTML representation of the message (null if the message level is below the given level)*/ 405 public String toHtmlString(int level) { return toHtmlString(level, true);} 406 /** HTML representation of the message*/ 407 public String toHtmlString(boolean includeDate) { return toHtmlString(MSGLEVEL_TRACE, includeDate); } 408 /** HTML representation of the message*/ 409 public String toHtmlString() { return toHtmlString(MSGLEVEL_TRACE, true); } 410 /** Saves message into an XML element */ 411 public void save(Element element) { 412 element.addAttribute("level", String.valueOf(iLevel)); 413 element.addAttribute("msg", iMessage); 414 element.addAttribute("date", String.valueOf(iDate.getTime())); 415 if (iStakTrace!=null) { 416 for (int i=0;i<iStakTrace.length;i++) 417 element.addElement("trace").setText(iStakTrace[i]); 418 } 419 } 420 } 421 /** Saves the message log into the given XML element */ 422 public void save(Element root) { 423 Element log = root.addElement("log"); 424 for (Enumeration e=iLog.elements();e.hasMoreElements();) { 425 Message m = (Message)e.nextElement(); 426 m.save(log.addElement("msg")); 427 } 428 } 429 /** Restores the message log from the given XML element */ 430 public void load(Element root, boolean clear) { 431 if (clear) iLog.clear(); 432 Element log = root.element("log"); 433 if (log!=null) { 434 for (Iterator i=log.elementIterator("msg");i.hasNext();) 435 iLog.addElement(new Message((Element)i.next())); 436 } 437 } 438 }