001 package net.sf.cpsolver.ifs.util; 002 003 import java.io.Serializable; 004 import java.text.SimpleDateFormat; 005 import java.util.ArrayList; 006 import java.util.Date; 007 import java.util.HashMap; 008 import java.util.Iterator; 009 import java.util.List; 010 011 import org.dom4j.Element; 012 013 /** 014 * Progress bar. <br> 015 * <br> 016 * Single instance class for recording the current state. It also allows 017 * recursive storing/restoring of the progress. 018 * 019 * <br> 020 * <br> 021 * Use: 022 * <ul> 023 * <code> 024 * Progress.getInstance().setStatus("Loading input data");<br> 025 * Progress.getInstance().setPhase("Creating variables ...", nrVariables);<br> 026 * for (int i=0;i<nrVariables;i++) {<br> 027 * //load variable here<br> 028 * Progress.getInstance().incProgress();<br> 029 * }<br> 030 * Progress.getInstance().setPhase("Creating constraints ...", nrConstraints);<br> 031 * for (int i=0;i<nrConstraints;i++) {<br> 032 * //load constraint here<br> 033 * Progress.getInstance().incProgress();<br> 034 * }<br> 035 * Progress.getInstance().setStatus("Solving problem");<br> 036 * ...<br> 037 * </code> 038 * </ul> 039 * 040 * @version IFS 1.2 (Iterative Forward Search)<br> 041 * Copyright (C) 2006 - 2010 Tomas Muller<br> 042 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 043 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 044 * <br> 045 * This library is free software; you can redistribute it and/or modify 046 * it under the terms of the GNU Lesser General Public License as 047 * published by the Free Software Foundation; either version 3 of the 048 * License, or (at your option) any later version. <br> 049 * <br> 050 * This library is distributed in the hope that it will be useful, but 051 * WITHOUT ANY WARRANTY; without even the implied warranty of 052 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 053 * Lesser General Public License for more details. <br> 054 * <br> 055 * You should have received a copy of the GNU Lesser General Public 056 * License along with this library; if not see 057 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 058 */ 059 public class Progress { 060 public static boolean sTraceEnabled = false; 061 private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(Progress.class); 062 public static SimpleDateFormat sDF = new SimpleDateFormat("MM/dd/yy HH:mm:ss.SSS"); 063 public static final int MSGLEVEL_TRACE = 0; 064 public static final int MSGLEVEL_DEBUG = 1; 065 public static final int MSGLEVEL_PROGRESS = 2; 066 public static final int MSGLEVEL_INFO = 3; 067 public static final int MSGLEVEL_STAGE = 4; 068 public static final int MSGLEVEL_WARN = 5; 069 public static final int MSGLEVEL_ERROR = 6; 070 public static final int MSGLEVEL_FATAL = 7; 071 072 private String iStatus = ""; 073 private String iPhase = ""; 074 private long iProgressMax = 0; 075 private long iProgressCurrent = 0; 076 private List<ProgressListener> iListeners = new ArrayList<ProgressListener>(5); 077 private List<Object[]> iSave = new ArrayList<Object[]>(5); 078 private List<Message> iLog = new ArrayList<Message>(1000); 079 private boolean iDisposed = false; 080 081 private static HashMap<Object, Progress> sInstances = new HashMap<Object, Progress>(); 082 083 private Progress() { 084 } 085 086 /** Progress default instance */ 087 public static Progress getInstance() { 088 return getInstance("--DEFAULT--"); 089 } 090 091 /** Progress instance */ 092 public static Progress getInstance(Object key) { 093 Progress progress = sInstances.get(key); 094 if (progress == null) { 095 progress = new Progress(); 096 sInstances.put(key, progress); 097 } 098 return progress; 099 } 100 101 /** Change progress instance for the given key */ 102 public static void changeInstance(Object oldKey, Object newKey) { 103 removeInstance(newKey); 104 Progress progress = sInstances.get(oldKey); 105 if (progress != null) { 106 sInstances.remove(oldKey); 107 sInstances.put(newKey, progress); 108 } 109 } 110 111 /** Remove progress instance for the given key */ 112 public static void removeInstance(Object key) { 113 Progress progress = sInstances.get(key); 114 if (progress != null) { 115 progress.iListeners.clear(); 116 progress.iDisposed = true; 117 sInstances.remove(key); 118 } 119 } 120 121 /** Current status */ 122 public String getStatus() { 123 return iStatus; 124 } 125 126 /** Sets current status */ 127 public void setStatus(String status) { 128 message(MSGLEVEL_STAGE, status); 129 if (!status.equals(iStatus)) { 130 iPhase = ""; 131 iProgressMax = 0; 132 iProgressCurrent = 0; 133 iStatus = status; 134 fireStatusChanged(); 135 } 136 } 137 138 /** Current phase */ 139 public String getPhase() { 140 return iPhase; 141 } 142 143 /** 144 * Sets current phase 145 * 146 * @param phase 147 * phase name 148 * @param progressMax 149 * maximum of progress bar 150 */ 151 public void setPhase(String phase, long progressMax) { 152 if (iSave.isEmpty() && !phase.equals(iPhase)) 153 message(MSGLEVEL_PROGRESS, phase); 154 iPhase = phase; 155 iProgressMax = progressMax; 156 iProgressCurrent = 0; 157 firePhaseChanged(); 158 } 159 160 /** 161 * Sets current phase. Maximum of progress bar is set to 100. 162 * 163 * @param phase 164 * phase name 165 */ 166 public void setPhase(String phase) { 167 setPhase(phase, 100); 168 } 169 170 /** 171 * Update progress bar. 172 * 173 * @param progress 174 * progress between 0 and progressMax 175 */ 176 public void setProgress(long progress) { 177 if (iProgressCurrent != progress) { 178 iProgressCurrent = progress; 179 fireProgressChanged(); 180 } 181 } 182 183 /** Current progress */ 184 public long getProgress() { 185 return iProgressCurrent; 186 } 187 188 /** Maximum of current progress */ 189 public long getProgressMax() { 190 return iProgressMax; 191 } 192 193 /** Increment current progress */ 194 public void incProgress() { 195 iProgressCurrent++; 196 fireProgressChanged(); 197 } 198 199 /** Adds progress listener */ 200 public void addProgressListener(ProgressListener listener) { 201 iListeners.add(listener); 202 } 203 204 /** Remove progress listener */ 205 public void removeProgressListener(ProgressListener listener) { 206 iListeners.remove(listener); 207 } 208 209 /** Remove all progress listeners */ 210 public void clearProgressListeners() { 211 iListeners.clear(); 212 } 213 214 /** Save current progress to the heap memory */ 215 public synchronized void save() { 216 iSave.add(new Object[] { iStatus, iPhase, new Long(iProgressMax), new Long(iProgressCurrent) }); 217 fireProgressSaved(); 218 } 219 220 /** Resore the progress from the heap memory */ 221 public synchronized void restore() { 222 if (iSave.isEmpty()) 223 return; 224 Object[] o = iSave.get(iSave.size() - 1); 225 iSave.remove(iSave.size() - 1); 226 iStatus = (String) o[0]; 227 iPhase = (String) o[1]; 228 iProgressMax = ((Long) o[2]).longValue(); 229 iProgressCurrent = ((Long) o[3]).longValue(); 230 fireProgressRestored(); 231 } 232 233 /** Prints a message */ 234 public void message(int level, String message, Throwable t) { 235 if (iDisposed) throw new RuntimeException("This solver is killed."); 236 Message m = new Message(level, message, t); 237 switch (level) { 238 case MSGLEVEL_TRACE: 239 sLogger.debug(" -- " + message, t); 240 break; 241 case MSGLEVEL_DEBUG: 242 sLogger.debug(" -- " + message, t); 243 break; 244 case MSGLEVEL_PROGRESS: 245 sLogger.debug("[" + message + "]", t); 246 break; 247 case MSGLEVEL_INFO: 248 sLogger.info(message, t); 249 break; 250 case MSGLEVEL_STAGE: 251 sLogger.info("[" + message + "]", t); 252 break; 253 case MSGLEVEL_WARN: 254 sLogger.warn(message, t); 255 break; 256 case MSGLEVEL_ERROR: 257 sLogger.error(message, t); 258 break; 259 case MSGLEVEL_FATAL: 260 sLogger.fatal(message, t); 261 break; 262 } 263 iLog.add(m); 264 fireMessagePrinted(m); 265 } 266 267 /** Prints a message */ 268 public void message(int level, String message) { 269 message(level, message, null); 270 } 271 272 /** Prints a trace message */ 273 public void trace(String message) { 274 if (!sTraceEnabled) 275 return; 276 message(MSGLEVEL_TRACE, message); 277 } 278 279 /** Prints a debug message */ 280 public void debug(String message) { 281 message(MSGLEVEL_DEBUG, message); 282 } 283 284 /** Prints an info message */ 285 public void info(String message) { 286 message(MSGLEVEL_INFO, message); 287 } 288 289 /** Prints a warning message */ 290 public void warn(String message) { 291 message(MSGLEVEL_WARN, message); 292 } 293 294 /** Prints an error message */ 295 public void error(String message) { 296 message(MSGLEVEL_ERROR, message); 297 } 298 299 /** Prints a fatal message */ 300 public void fatal(String message) { 301 message(MSGLEVEL_FATAL, message); 302 } 303 304 /** Prints a trace message */ 305 public void trace(String message, Throwable e) { 306 if (!sTraceEnabled) 307 return; 308 message(MSGLEVEL_TRACE, message, e); 309 } 310 311 /** Prints a debug message */ 312 public void debug(String message, Throwable e) { 313 message(MSGLEVEL_DEBUG, message, e); 314 } 315 316 /** Prints an info message */ 317 public void info(String message, Throwable e) { 318 message(MSGLEVEL_INFO, message, e); 319 } 320 321 /** Prints a warning message */ 322 public void warn(String message, Throwable e) { 323 message(MSGLEVEL_WARN, message, e); 324 } 325 326 /** Prints an error message */ 327 public void error(String message, Throwable e) { 328 message(MSGLEVEL_ERROR, message, e); 329 } 330 331 /** Prints a fatal message */ 332 public void fatal(String message, Throwable e) { 333 message(MSGLEVEL_FATAL, message, e); 334 } 335 336 /** Returns log (list of messages) */ 337 public List<Message> getLog() { 338 return iLog; 339 } 340 341 /** 342 * Returns log (list of messages). Only messages with the given level or 343 * higher are included. 344 */ 345 public String getLog(int level) { 346 StringBuffer sb = new StringBuffer(); 347 for (Message m : iLog) { 348 String s = m.toString(level); 349 if (s != null) 350 sb.append(s + "\n"); 351 } 352 return sb.toString(); 353 } 354 355 /** Returns log in HTML format */ 356 public String getHtmlLog(int level, boolean includeDate) { 357 StringBuffer sb = new StringBuffer(); 358 for (Message m : iLog) { 359 String s = m.toHtmlString(level, includeDate); 360 if (s != null) 361 sb.append(s + "<br>"); 362 } 363 return sb.toString(); 364 } 365 366 /** 367 * Returns log in HTML format (only messages with the given level or higher 368 * are included) 369 */ 370 public String getHtmlLog(int level, boolean includeDate, String fromStage) { 371 StringBuffer sb = new StringBuffer(); 372 for (Message m : iLog) { 373 if (m.getLevel() == MSGLEVEL_STAGE && m.getMessage().equals(fromStage)) 374 sb = new StringBuffer(); 375 String s = m.toHtmlString(level, includeDate); 376 if (s != null) 377 sb.append(s + "<br>"); 378 } 379 return sb.toString(); 380 } 381 382 /** Clear the log */ 383 public void clear() { 384 iLog.clear(); 385 } 386 387 private void fireStatusChanged() { 388 for (ProgressListener listener : iListeners) { 389 listener.statusChanged(iStatus); 390 } 391 } 392 393 private void firePhaseChanged() { 394 for (ProgressListener listener : iListeners) { 395 listener.phaseChanged(iPhase); 396 } 397 } 398 399 private void fireProgressChanged() { 400 for (ProgressListener listener : iListeners) { 401 listener.progressChanged(iProgressCurrent, iProgressMax); 402 } 403 } 404 405 private void fireProgressSaved() { 406 for (ProgressListener listener : iListeners) { 407 listener.progressSaved(); 408 } 409 } 410 411 private void fireProgressRestored() { 412 for (ProgressListener listener : iListeners) { 413 listener.progressRestored(); 414 } 415 } 416 417 private void fireMessagePrinted(Message message) { 418 for (ProgressListener listener : iListeners) { 419 listener.progressMessagePrinted(message); 420 } 421 } 422 423 /** Log nessage */ 424 public static class Message implements Serializable { 425 private static final long serialVersionUID = 1L; 426 private int iLevel = 0; 427 private String iMessage; 428 private Date iDate = null; 429 private String[] iStakTrace = null; 430 431 private Message(int level, String message, Throwable e) { 432 iLevel = level; 433 iMessage = message; 434 iDate = new Date(); 435 if (e != null) { 436 StackTraceElement trace[] = e.getStackTrace(); 437 if (trace != null) { 438 iStakTrace = new String[trace.length + 1]; 439 iStakTrace[0] = e.getClass().getName() + ": " + e.getMessage(); 440 for (int i = 0; i < trace.length; i++) 441 iStakTrace[i + 1] = trace[i].getClassName() 442 + "." 443 + trace[i].getMethodName() 444 + (trace[i].getFileName() == null ? "" : "(" + trace[i].getFileName() 445 + (trace[i].getLineNumber() >= 0 ? ":" + trace[i].getLineNumber() : "") + ")"); 446 } 447 } 448 } 449 450 /** Creates message out of XML element */ 451 public Message(Element element) { 452 iLevel = Integer.parseInt(element.attributeValue("level", "0")); 453 iMessage = element.attributeValue("msg"); 454 iDate = new Date(Long.parseLong(element.attributeValue("date", "0"))); 455 java.util.List<?> tr = element.elements("trace"); 456 if (tr != null && !tr.isEmpty()) { 457 iStakTrace = new String[tr.size()]; 458 for (int i = 0; i < tr.size(); i++) 459 iStakTrace[i] = ((Element) tr.get(i)).getText(); 460 } 461 } 462 463 /** Message */ 464 public String getMessage() { 465 return iMessage; 466 } 467 468 /** Debug level */ 469 public int getLevel() { 470 return iLevel; 471 } 472 473 /** Time stamp */ 474 public Date getDate() { 475 return iDate; 476 } 477 478 /** Tracelog */ 479 private String getTraceLog() { 480 if (iStakTrace == null) 481 return ""; 482 StringBuffer ret = new StringBuffer("\n" + iStakTrace[0]); 483 for (int i = 1; i < iStakTrace.length; i++) 484 ret.append("\n at " + iStakTrace[i]); 485 return ret.toString(); 486 } 487 488 /** Tracelog as HTML */ 489 private String getHtmlTraceLog() { 490 if (iStakTrace == null) 491 return ""; 492 StringBuffer ret = new StringBuffer("<BR>" + iStakTrace[0]); 493 for (int i = 1; i < iStakTrace.length; i++) 494 ret.append("<BR> at " + iStakTrace[i]); 495 return ret.toString(); 496 } 497 498 /** 499 * String representation of the message (null if the message level is 500 * below the given level) 501 */ 502 public String toString(int level) { 503 if (iLevel < level) 504 return null; 505 switch (iLevel) { 506 case MSGLEVEL_TRACE: 507 return sDF.format(iDate) + " -- " + iMessage + getTraceLog(); 508 case MSGLEVEL_DEBUG: 509 return sDF.format(iDate) + " -- " + iMessage + getTraceLog(); 510 case MSGLEVEL_PROGRESS: 511 return sDF.format(iDate) + " [" + iMessage + "]" + getTraceLog(); 512 case MSGLEVEL_INFO: 513 return sDF.format(iDate) + " " + iMessage + getTraceLog(); 514 case MSGLEVEL_STAGE: 515 return sDF.format(iDate) + " >>> " + iMessage + " <<<" + getTraceLog(); 516 case MSGLEVEL_WARN: 517 return sDF.format(iDate) + " WARNING: " + iMessage + getTraceLog(); 518 case MSGLEVEL_ERROR: 519 return sDF.format(iDate) + " ERROR: " + iMessage + getTraceLog(); 520 case MSGLEVEL_FATAL: 521 return sDF.format(iDate) + " >>>FATAL: " + iMessage + " <<<" + getTraceLog(); 522 } 523 return null; 524 } 525 526 /** String representation of the message */ 527 @Override 528 public String toString() { 529 return toString(MSGLEVEL_TRACE); 530 } 531 532 /** HTML representation of the message */ 533 public String toHtmlString(int level, boolean includeDate) { 534 if (iLevel < level) 535 return null; 536 switch (iLevel) { 537 case MSGLEVEL_TRACE: 538 return (includeDate ? sDF.format(iDate) : "") + " -- " + iMessage 539 + getHtmlTraceLog(); 540 case MSGLEVEL_DEBUG: 541 return (includeDate ? sDF.format(iDate) : "") + " -- " + iMessage + getHtmlTraceLog(); 542 case MSGLEVEL_PROGRESS: 543 return (includeDate ? sDF.format(iDate) : "") + " " + iMessage + getHtmlTraceLog(); 544 case MSGLEVEL_INFO: 545 return (includeDate ? sDF.format(iDate) : "") + " " + iMessage + getHtmlTraceLog(); 546 case MSGLEVEL_STAGE: 547 return "<br>" + (includeDate ? sDF.format(iDate) : "") + " <span style='font-weight:bold;'>" 548 + iMessage + "</span>" + getHtmlTraceLog(); 549 case MSGLEVEL_WARN: 550 return (includeDate ? sDF.format(iDate) : "") 551 + " <span style='color:orange;font-weight:bold;'>WARNING:</span> " + iMessage 552 + getHtmlTraceLog(); 553 case MSGLEVEL_ERROR: 554 return (includeDate ? sDF.format(iDate) : "") 555 + " <span style='color:red;font-weight:bold;'>ERROR:</span> " + iMessage 556 + getHtmlTraceLog(); 557 case MSGLEVEL_FATAL: 558 return (includeDate ? sDF.format(iDate) : "") 559 + " <span style='color:red;font-weight:bold;'>>>>FATAL: " + iMessage 560 + " <<<</span>" + getHtmlTraceLog(); 561 } 562 return null; 563 } 564 565 /** 566 * HTML representation of the message (null if the message level is 567 * below the given level) 568 */ 569 public String toHtmlString(int level) { 570 return toHtmlString(level, true); 571 } 572 573 /** HTML representation of the message */ 574 public String toHtmlString(boolean includeDate) { 575 return toHtmlString(MSGLEVEL_TRACE, includeDate); 576 } 577 578 /** HTML representation of the message */ 579 public String toHtmlString() { 580 return toHtmlString(MSGLEVEL_TRACE, true); 581 } 582 583 /** Saves message into an XML element */ 584 public void save(Element element) { 585 element.addAttribute("level", String.valueOf(iLevel)); 586 element.addAttribute("msg", iMessage); 587 element.addAttribute("date", String.valueOf(iDate.getTime())); 588 if (iStakTrace != null) { 589 for (int i = 0; i < iStakTrace.length; i++) 590 element.addElement("trace").setText(iStakTrace[i]); 591 } 592 } 593 } 594 595 /** Saves the message log into the given XML element */ 596 public void save(Element root) { 597 Element log = root.addElement("log"); 598 for (Message m : iLog) { 599 m.save(log.addElement("msg")); 600 } 601 } 602 603 /** Restores the message log from the given XML element */ 604 public void load(Element root, boolean clear) { 605 if (clear) 606 iLog.clear(); 607 Element log = root.element("log"); 608 if (log != null) { 609 for (Iterator<?> i = log.elementIterator("msg"); i.hasNext();) 610 iLog.add(new Message((Element) i.next())); 611 } 612 } 613 }