package org.juxtasoftware.util; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.Map; import javax.swing.BoundedRangeModel; import javax.swing.DefaultBoundedRangeModel; import org.json.simple.JSONObject; import com.google.common.base.Objects; import com.google.common.collect.Maps; public class BackgroundTaskStatus { public enum Status {PENDING, PROCESSING, COMPLETE, CANCEL_REQUESTED, CANCELLED, FAILED}; private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); private final BoundedRangeModel boundedRangeModel = new DefaultBoundedRangeModel(); private final String title; private String note; private Status status; private final Map<Integer, BackgroundTaskSegment> segments = Maps.newLinkedHashMap(); public BackgroundTaskStatus(String title) { this.title = title; this.boundedRangeModel.setMinimum(0); this.boundedRangeModel.setMaximum(100); this.status = Status.PENDING; } public BackgroundTaskSegment add(int weight, BackgroundTaskSegment segment) { this.status = Status.PROCESSING; this.segments.put(weight, segment); updateValue(); segment.addChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { updateValue(); } }); return segment; } public void begin() { if ( this.status.equals(Status.PENDING)) { this.status = Status.PROCESSING; } } public void finish() { this.status = Status.COMPLETE; } public boolean isActive() { return ( this.status.equals(Status.PENDING) || this.status.equals(Status.PROCESSING) ); } public void fail( final String reason ) { if ( isActive() ) { this.status = Status.FAILED; setNote( JSONObject.escape(reason) ); } } public void cancel() { if ( isActive() ) { this.status = Status.CANCEL_REQUESTED; } } public final Status getStatus() { return this.status; } public String getNote() { return this.note; } public void setNote(String note) { note = (note == null ? title : note); if (!Objects.equal(this.note, note)) { final String oldValue = this.note; propertyChangeSupport.firePropertyChange("note", oldValue, this.note = note); } } public BoundedRangeModel getBoundedRangeModel() { return boundedRangeModel; } private void updateValue() { float value = 0; int totalWeight = 0; for (Map.Entry<Integer, BackgroundTaskSegment> te : segments.entrySet()) { final Integer weight = te.getKey(); totalWeight += weight; final BackgroundTaskSegment segment = te.getValue(); final int maxValue = segment.getMaxValue(); if (maxValue > 0) { value += weight * (segment.getValue() / (maxValue * 1.0f)); } } this.boundedRangeModel.setValue(Math.round(100 * (value / totalWeight))); // Check for status changes... if (this.status.equals(Status.CANCEL_REQUESTED)) { this.status = Status.CANCELLED; throw new BackgroundTaskCanceledException(); } else if ( this.boundedRangeModel.getValue() == this.boundedRangeModel.getMaximum() ) { this.status = Status.COMPLETE; } } public void addChangeListener(PropertyChangeListener l) { propertyChangeSupport.addPropertyChangeListener(l); } public void removeChangeListener(PropertyChangeListener l) { propertyChangeSupport.removePropertyChangeListener(l); } @Override public String toString() { return this.status.toString(); } }