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 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 }