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     * &nbsp;&nbsp;&nbsp;&nbsp;//load variable here<br>
019     * &nbsp;&nbsp;&nbsp;&nbsp;Progress.getInstance().incProgress();<br>
020     * }<br>
021     * Progress.getInstance().setPhase("Creating constraints ...", nrConstraints);<br>
022     * for (int i=0;i<nrConstraints;i++) {<br>
023     * &nbsp;&nbsp;&nbsp;&nbsp;//load constraint here<br>
024     * &nbsp;&nbsp;&nbsp;&nbsp;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>&nbsp;&nbsp;&nbsp;&nbsp;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):"")+" &nbsp;&nbsp;&nbsp;&nbsp;-- "+iMessage+getHtmlTraceLog();
394                            case MSGLEVEL_DEBUG     : return (includeDate?sDF.format(iDate):"")+" &nbsp;&nbsp;-- "+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;'>&gt;&gt;&gt;FATAL: "+iMessage+" &lt;&lt;&lt;</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    }