package er.ajax; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import com.webobjects.appserver.WOSession; import com.webobjects.foundation.NSDictionary; import com.webobjects.foundation.NSMutableDictionary; import er.extensions.formatters.ERXUnitAwareDecimalFormat; /** * AjaxProgress is the model for an AjaxProgressBar. By holding * onto this, you can keep track of and control the progress * of whatever operation is bound to this progress object. * * @author mschrag */ public class AjaxProgress { /** * Do I need to update serialVersionUID? * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a> */ private static final long serialVersionUID = 1L; private String _id; private long _value; private long _maximum; private boolean _done; private Throwable _failure; private boolean _canceled; private boolean _completionEventsFired; private boolean _reset; private String _status; /** * Construct an AjaxProgress * * @param maximum the maximum value of this progress */ public AjaxProgress(long maximum) { this("AjaxProgress" + System.currentTimeMillis(), maximum); } /** * Construct an AjaxProgress * * @param id the id of this AjaxProgress (only useful if you're registering it with AjaxProgressBar's registry) * @param maximum the maximum value of this progress */ public AjaxProgress(String id, long maximum) { _id = id; _maximum = maximum; } /** * Returns the id of this progress model. * * @return the id of this progress model */ public String id() { return _id; } /** * Sets the current value of this progress model. In the context of * an upload, value would represent the number of bytes uploaded * so far. * * @param value the new value */ public void setValue(long value) { _value = value; } /** * Returns the current value of this progress model. If this * model isSucceeded, then this will return maximum(). * * @return the current value of this progress model */ public long value() { long value; if (isSucceeded()) { value = maximum(); } else { value = _value; } return value; } /** * Increment value by the given amount. * * @param count the mount to increment value by */ public void incrementValue(long count) { _value += count; } /** * Sets the maximum value for this progress model. * * @param maximum the maximum value for this progress model */ public void setMaximum(long maximum) { _maximum = maximum; } /** * Returns the maximum value for this progress model. * * @return the maximum value for this progress model */ public long maximum() { return _maximum; } /** * Returns the percentage completion of this progress model (0.0 - 1.0). * * @return the percentage completion of this progress model (0.0 - 1.0) */ public double percentage() { double value = value(); double maximum = maximum(); double percentage = 0; if (maximum > 0) { percentage = (value / maximum); } return percentage; } /** * Returns whether or not this procedure has started. * * @return whether or not this procedure has started */ public boolean isStarted() { return _value > 0 || isDone(); } /** * Sets whether or not this procedure is done. * * @param done whether or not this procedure is done */ public void setDone(boolean done) { _done = done; } /** * Returns whether or not this procedure is done. * * @return whether or not this procedure is done */ public boolean isDone() { return _done; } /** * Sets the exception that caused this procedure to fail. * * @param failure the exception that caused this procedure to fail */ public void setFailure(Throwable failure) { _failure = failure; } /** * Returns the exception that caused this procedure to fail. * @return the exception that caused this procedure to fail */ public Throwable failure() { return _failure; } /** * Cancels this procedure. */ public void cancel() { _canceled = true; } /** * Returns true if this procedure was canceled. * @return true if this procedure was canceled */ public boolean isCanceled() { return _canceled; } /** * Returns true if this procedure failed (and was not canceled). * @return true if this procedure failed (and was not canceled) */ public boolean isFailed() { return !_canceled && _failure != null; } /** * Returns true if this procedure is done, not canceled, and not failed. * * @return true if this procedure is done, not canceled, and not failed */ public boolean isSucceeded() { return _done && !_reset && !_canceled && _failure == null; } /** * Disposes any resources associated with this procedure. */ public void dispose() { _completionEventsFired = false; } /** * Sets whether or not this procedure has notified listeners of its completion. * * @param completionEventsFired whether or not this procedure has notified listeners of its completion */ public void setCompletionEventsFired(boolean completionEventsFired) { _completionEventsFired = completionEventsFired; } /** * Returns whether or not this procedure has notified listeners of its completion. * @return whether or not this procedure has notified listeners of its completion */ public boolean completionEventsFired() { return _completionEventsFired; } /** * Flags the attached procedure to reset the next time it is processed. */ public void reset() { _reset = true; } /** * Returns whether or not the attached procedure should reset the next time it is processed. * * @return whether or not the attached procedure should reset the next time it is processed */ public boolean shouldReset() { return _reset; } /** * Sets the current status message for this process. * * @param status the current status message for this process */ public void setStatus(String status) { _status = status; } /** * Returns the current status message for this process. * * @return the current status message for this process */ public String status() { return _status; } /** * Convenience method for copying a stream and tracking it with this progress model. * * @param inputStream the input stream to copy from * @param outputStream the output stream to copy to * @param maxSize the maximum size to read * @throws IOException if there is a failure */ public void copyAndTrack(InputStream inputStream, OutputStream outputStream, long maxSize) throws IOException { byte[] buffer = new byte[64 * 1024]; try { boolean done = false; do { int bytesRead = inputStream.read(buffer); if (bytesRead <= 0) { done = true; } else { incrementValue(bytesRead); if (maxSize > 0 && _value > maxSize) { throw new IOException("The provided stream exceeded the maximum length of " + new ERXUnitAwareDecimalFormat(ERXUnitAwareDecimalFormat.BYTE).format(maxSize) + " bytes."); } outputStream.write(buffer, 0, bytesRead); } } while (!done && !isCanceled() && !shouldReset()); if (isCanceled() || shouldReset()) { dispose(); } } catch (IOException e) { dispose(); setFailure(e); throw e; } catch (RuntimeException e) { dispose(); setFailure(e); throw e; } } /** * Register a progress object in the registry. * * @param session * the session * @param progress * the progress object to register */ public static void registerProgress(WOSession session, AjaxProgress progress) { NSMutableDictionary progresses = (NSMutableDictionary) session.objectForKey(AjaxProgressBar.AJAX_PROGRESSES_KEY); if (progresses == null) { progresses = new NSMutableDictionary(); session.setObjectForKey(progresses, AjaxProgressBar.AJAX_PROGRESSES_KEY); } progresses.setObjectForKey(progress, progress.id()); } /** * Unregister a progress object from the registry. * * @param session * the session * @param progress * the progress object to unregister */ public static void unregisterProgress(WOSession session, AjaxProgress progress) { NSMutableDictionary progresses = (NSMutableDictionary) session.objectForKey(AjaxProgressBar.AJAX_PROGRESSES_KEY); if (progresses != null && progress.id() != null) { progresses.removeObjectForKey(progress.id()); } } /** * Returns the progress object with the given id (or null if one does not exist). * * @param session * the session * @param id * the id of the progress to retrieve * @return the matching progess object (or null) */ public static AjaxProgress progress(WOSession session, String id) { AjaxProgress progress = null; NSDictionary progresses = (NSDictionary) session.objectForKey(AjaxProgressBar.AJAX_PROGRESSES_KEY); if (progresses != null) { progress = (AjaxProgress) progresses.objectForKey(id); } return progress; } }