001package net.sf.cpsolver.ifs.util; 002 003import java.io.Serializable; 004import java.text.SimpleDateFormat; 005import java.util.ArrayList; 006import java.util.Date; 007import java.util.HashMap; 008import java.util.Iterator; 009import java.util.List; 010 011import 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 Tomáš Müller<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 */ 059public 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 synchronized (iLog) { 264 iLog.add(m); 265 } 266 fireMessagePrinted(m); 267 } 268 269 /** Prints a message */ 270 public void message(int level, String message) { 271 message(level, message, null); 272 } 273 274 /** Prints a trace message */ 275 public void trace(String message) { 276 if (!sTraceEnabled) 277 return; 278 message(MSGLEVEL_TRACE, message); 279 } 280 281 /** Prints a debug message */ 282 public void debug(String message) { 283 message(MSGLEVEL_DEBUG, message); 284 } 285 286 /** Prints an info message */ 287 public void info(String message) { 288 message(MSGLEVEL_INFO, message); 289 } 290 291 /** Prints a warning message */ 292 public void warn(String message) { 293 message(MSGLEVEL_WARN, message); 294 } 295 296 /** Prints an error message */ 297 public void error(String message) { 298 message(MSGLEVEL_ERROR, message); 299 } 300 301 /** Prints a fatal message */ 302 public void fatal(String message) { 303 message(MSGLEVEL_FATAL, message); 304 } 305 306 /** Prints a trace message */ 307 public void trace(String message, Throwable e) { 308 if (!sTraceEnabled) 309 return; 310 message(MSGLEVEL_TRACE, message, e); 311 } 312 313 /** Prints a debug message */ 314 public void debug(String message, Throwable e) { 315 message(MSGLEVEL_DEBUG, message, e); 316 } 317 318 /** Prints an info message */ 319 public void info(String message, Throwable e) { 320 message(MSGLEVEL_INFO, message, e); 321 } 322 323 /** Prints a warning message */ 324 public void warn(String message, Throwable e) { 325 message(MSGLEVEL_WARN, message, e); 326 } 327 328 /** Prints an error message */ 329 public void error(String message, Throwable e) { 330 message(MSGLEVEL_ERROR, message, e); 331 } 332 333 /** Prints a fatal message */ 334 public void fatal(String message, Throwable e) { 335 message(MSGLEVEL_FATAL, message, e); 336 } 337 338 /** Returns log (list of messages) */ 339 public List<Message> getLog() { 340 return iLog; 341 } 342 343 /** 344 * Returns log (list of messages). Only messages with the given level or 345 * higher are included. 346 */ 347 public String getLog(int level) { 348 StringBuffer sb = new StringBuffer(); 349 synchronized (iLog) { 350 for (Message m : iLog) { 351 String s = m.toString(level); 352 if (s != null) 353 sb.append(s + "\n"); 354 } 355 } 356 return sb.toString(); 357 } 358 359 /** Returns log in HTML format */ 360 public String getHtmlLog(int level, boolean includeDate) { 361 StringBuffer sb = new StringBuffer(); 362 synchronized (iLog) { 363 for (Message m : iLog) { 364 String s = m.toHtmlString(level, includeDate); 365 if (s != null) 366 sb.append(s + "<br>"); 367 } 368 } 369 return sb.toString(); 370 } 371 372 /** 373 * Returns log in HTML format (only messages with the given level or higher 374 * are included) 375 */ 376 public String getHtmlLog(int level, boolean includeDate, String fromStage) { 377 StringBuffer sb = new StringBuffer(); 378 synchronized (iLog) { 379 for (Message m : iLog) { 380 if (m.getLevel() == MSGLEVEL_STAGE && m.getMessage().equals(fromStage)) 381 sb = new StringBuffer(); 382 String s = m.toHtmlString(level, includeDate); 383 if (s != null) 384 sb.append(s + "<br>"); 385 } 386 } 387 return sb.toString(); 388 } 389 390 /** Clear the log */ 391 public void clear() { 392 synchronized (iLog) { 393 iLog.clear(); 394 } 395 } 396 397 private void fireStatusChanged() { 398 for (ProgressListener listener : iListeners) { 399 listener.statusChanged(iStatus); 400 } 401 } 402 403 private void firePhaseChanged() { 404 for (ProgressListener listener : iListeners) { 405 listener.phaseChanged(iPhase); 406 } 407 } 408 409 private void fireProgressChanged() { 410 for (ProgressListener listener : iListeners) { 411 listener.progressChanged(iProgressCurrent, iProgressMax); 412 } 413 } 414 415 private void fireProgressSaved() { 416 for (ProgressListener listener : iListeners) { 417 listener.progressSaved(); 418 } 419 } 420 421 private void fireProgressRestored() { 422 for (ProgressListener listener : iListeners) { 423 listener.progressRestored(); 424 } 425 } 426 427 private void fireMessagePrinted(Message message) { 428 for (ProgressListener listener : iListeners) { 429 listener.progressMessagePrinted(message); 430 } 431 } 432 433 /** Log nessage */ 434 public static class Message implements Serializable { 435 private static final long serialVersionUID = 1L; 436 private int iLevel = 0; 437 private String iMessage; 438 private Date iDate = null; 439 private String[] iStakTrace = null; 440 441 private Message(int level, String message, Throwable e) { 442 iLevel = level; 443 iMessage = message; 444 iDate = new Date(); 445 if (e != null) { 446 StackTraceElement trace[] = e.getStackTrace(); 447 if (trace != null) { 448 iStakTrace = new String[trace.length + 1]; 449 iStakTrace[0] = e.getClass().getName() + ": " + e.getMessage(); 450 for (int i = 0; i < trace.length; i++) 451 iStakTrace[i + 1] = trace[i].getClassName() 452 + "." 453 + trace[i].getMethodName() 454 + (trace[i].getFileName() == null ? "" : "(" + trace[i].getFileName() 455 + (trace[i].getLineNumber() >= 0 ? ":" + trace[i].getLineNumber() : "") + ")"); 456 } 457 } 458 } 459 460 /** Creates message out of XML element */ 461 public Message(Element element) { 462 iLevel = Integer.parseInt(element.attributeValue("level", "0")); 463 iMessage = element.attributeValue("msg"); 464 iDate = new Date(Long.parseLong(element.attributeValue("date", "0"))); 465 java.util.List<?> tr = element.elements("trace"); 466 if (tr != null && !tr.isEmpty()) { 467 iStakTrace = new String[tr.size()]; 468 for (int i = 0; i < tr.size(); i++) 469 iStakTrace[i] = ((Element) tr.get(i)).getText(); 470 } 471 } 472 473 /** Message */ 474 public String getMessage() { 475 return iMessage; 476 } 477 478 /** Debug level */ 479 public int getLevel() { 480 return iLevel; 481 } 482 483 /** Time stamp */ 484 public Date getDate() { 485 return iDate; 486 } 487 488 /** Tracelog */ 489 private String getTraceLog() { 490 if (iStakTrace == null) 491 return ""; 492 StringBuffer ret = new StringBuffer("\n" + iStakTrace[0]); 493 for (int i = 1; i < iStakTrace.length; i++) 494 ret.append("\n at " + iStakTrace[i]); 495 return ret.toString(); 496 } 497 498 /** Tracelog as HTML */ 499 private String getHtmlTraceLog() { 500 if (iStakTrace == null) 501 return ""; 502 StringBuffer ret = new StringBuffer("<BR>" + iStakTrace[0]); 503 for (int i = 1; i < iStakTrace.length; i++) 504 ret.append("<BR> at " + iStakTrace[i]); 505 return ret.toString(); 506 } 507 508 /** 509 * String representation of the message (null if the message level is 510 * below the given level) 511 */ 512 public String toString(int level) { 513 if (iLevel < level) 514 return null; 515 switch (iLevel) { 516 case MSGLEVEL_TRACE: 517 return sDF.format(iDate) + " -- " + iMessage + getTraceLog(); 518 case MSGLEVEL_DEBUG: 519 return sDF.format(iDate) + " -- " + iMessage + getTraceLog(); 520 case MSGLEVEL_PROGRESS: 521 return sDF.format(iDate) + " [" + iMessage + "]" + getTraceLog(); 522 case MSGLEVEL_INFO: 523 return sDF.format(iDate) + " " + iMessage + getTraceLog(); 524 case MSGLEVEL_STAGE: 525 return sDF.format(iDate) + " >>> " + iMessage + " <<<" + getTraceLog(); 526 case MSGLEVEL_WARN: 527 return sDF.format(iDate) + " WARNING: " + iMessage + getTraceLog(); 528 case MSGLEVEL_ERROR: 529 return sDF.format(iDate) + " ERROR: " + iMessage + getTraceLog(); 530 case MSGLEVEL_FATAL: 531 return sDF.format(iDate) + " >>>FATAL: " + iMessage + " <<<" + getTraceLog(); 532 } 533 return null; 534 } 535 536 /** String representation of the message */ 537 @Override 538 public String toString() { 539 return toString(MSGLEVEL_TRACE); 540 } 541 542 /** HTML representation of the message */ 543 public String toHtmlString(int level, boolean includeDate) { 544 if (iLevel < level) 545 return null; 546 switch (iLevel) { 547 case MSGLEVEL_TRACE: 548 return (includeDate ? sDF.format(iDate) : "") + " -- " + iMessage 549 + getHtmlTraceLog(); 550 case MSGLEVEL_DEBUG: 551 return (includeDate ? sDF.format(iDate) : "") + " -- " + iMessage + getHtmlTraceLog(); 552 case MSGLEVEL_PROGRESS: 553 return (includeDate ? sDF.format(iDate) : "") + " " + iMessage + getHtmlTraceLog(); 554 case MSGLEVEL_INFO: 555 return (includeDate ? sDF.format(iDate) : "") + " " + iMessage + getHtmlTraceLog(); 556 case MSGLEVEL_STAGE: 557 return "<br>" + (includeDate ? sDF.format(iDate) : "") + " <span style='font-weight:bold;'>" 558 + iMessage + "</span>" + getHtmlTraceLog(); 559 case MSGLEVEL_WARN: 560 return (includeDate ? sDF.format(iDate) : "") 561 + " <span style='color:orange;font-weight:bold;'>WARNING:</span> " + iMessage 562 + getHtmlTraceLog(); 563 case MSGLEVEL_ERROR: 564 return (includeDate ? sDF.format(iDate) : "") 565 + " <span style='color:red;font-weight:bold;'>ERROR:</span> " + iMessage 566 + getHtmlTraceLog(); 567 case MSGLEVEL_FATAL: 568 return (includeDate ? sDF.format(iDate) : "") 569 + " <span style='color:red;font-weight:bold;'>>>>FATAL: " + iMessage 570 + " <<<</span>" + getHtmlTraceLog(); 571 } 572 return null; 573 } 574 575 /** 576 * HTML representation of the message (null if the message level is 577 * below the given level) 578 */ 579 public String toHtmlString(int level) { 580 return toHtmlString(level, true); 581 } 582 583 /** HTML representation of the message */ 584 public String toHtmlString(boolean includeDate) { 585 return toHtmlString(MSGLEVEL_TRACE, includeDate); 586 } 587 588 /** HTML representation of the message */ 589 public String toHtmlString() { 590 return toHtmlString(MSGLEVEL_TRACE, true); 591 } 592 593 /** Saves message into an XML element */ 594 public void save(Element element) { 595 element.addAttribute("level", String.valueOf(iLevel)); 596 element.addAttribute("msg", iMessage); 597 element.addAttribute("date", String.valueOf(iDate.getTime())); 598 if (iStakTrace != null) { 599 for (int i = 0; i < iStakTrace.length; i++) 600 element.addElement("trace").setText(iStakTrace[i]); 601 } 602 } 603 } 604 605 /** Saves the message log into the given XML element */ 606 public void save(Element root) { 607 Element log = root.addElement("log"); 608 synchronized (iLog) { 609 for (Message m : iLog) { 610 m.save(log.addElement("msg")); 611 } 612 } 613 } 614 615 /** Restores the message log from the given XML element */ 616 public void load(Element root, boolean clear) { 617 synchronized (iLog) { 618 if (clear) 619 iLog.clear(); 620 Element log = root.element("log"); 621 if (log != null) { 622 for (Iterator<?> i = log.elementIterator("msg"); i.hasNext();) 623 iLog.add(new Message((Element) i.next())); 624 } 625 } 626 } 627}