001 /*
002 * JBoss, Home of Professional Open Source.
003 * Copyright 2008, Red Hat Middleware LLC, and individual contributors
004 * as indicated by the @author tags. See the copyright.txt file in the
005 * distribution for a full listing of individual contributors.
006 *
007 * This is free software; you can redistribute it and/or modify it
008 * under the terms of the GNU Lesser General Public License as
009 * published by the Free Software Foundation; either version 2.1 of
010 * the License, or (at your option) any later version.
011 *
012 * This software is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this software; if not, write to the Free
019 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021 */
022
023 package org.jboss.dna.common.monitor;
024
025 import java.io.Serializable;
026 import java.text.DecimalFormat;
027 import net.jcip.annotations.Immutable;
028 import org.jboss.dna.common.util.StringUtil;
029
030 /**
031 * A snapshot of the progress on an activity.
032 *
033 * @author Randall Hauch
034 */
035 @Immutable
036 public class ProgressStatus implements Serializable, Comparable<ProgressStatus> {
037
038 /**
039 */
040 private static final long serialVersionUID = -7771764546193063275L;
041 protected static final String PERCENTAGE_PATTERN = "##0.0"; // percentage should always fit
042 protected static final double PERCENT_PRECISION = 0.001d;
043
044 /**
045 * Compute the percentage worked, accounting for imprecision in
046 *
047 * @param workedSoFar the amount of work so far percentage worked
048 * @param totalWork the total amount of work for this activity
049 * @return the percentage worked
050 */
051 protected static double computePercentage( double workedSoFar,
052 double totalWork ) {
053 if (isSamePercentage(workedSoFar, 0.0d)) return 0.0d;
054 assert totalWork > 0.0d;
055 double percentage = workedSoFar / totalWork * 100.0d;
056 if (isSamePercentage(percentage, 100.0d)) percentage = 100.0d;
057 return percentage;
058 }
059
060 protected static boolean isSamePercentage( double percentage1,
061 double percentage2 ) {
062 return Math.abs(percentage1 - percentage2) <= PERCENT_PRECISION;
063 }
064
065 private final String activityName;
066 private final double percentWorked;
067 private final boolean done;
068 private final boolean cancelled;
069 private final String message;
070
071 /**
072 * Create the progress status.
073 *
074 * @param activityName the name of the activity, which may not be null
075 * @param message the message for the progress, which may not be null
076 * @param percentWorked the percentage worked, ranging from 0.0 for not started to 100.0 for complete; a negative value are
077 * treated as 0.0, while a value greater than 100.0 is treated as 100.0
078 * @param cancelled true if the activity has been requested to be cancelled, or false otherwise
079 */
080 public ProgressStatus( String activityName,
081 String message,
082 double percentWorked,
083 boolean cancelled ) {
084 assert activityName != null;
085 assert message != null;
086 this.activityName = activityName;
087 this.done = percentWorked >= 100.0d;
088 this.percentWorked = this.done ? 100.0d : (percentWorked <= 0.0d ? 0.0d : percentWorked);
089 this.message = message;
090 this.cancelled = cancelled;
091 }
092
093 /**
094 * Create the progress status and compute the percentage worked.
095 *
096 * @param activityName the name of the activity, which may not be null
097 * @param message the message for the progress, which may not be null
098 * @param workedSoFar the amount of work so far percentage worked
099 * @param totalWork the total amount of work for this activity
100 * @param cancelled true if the activity has been requested to be cancelled, or false otherwise
101 */
102 public ProgressStatus( String activityName,
103 String message,
104 double workedSoFar,
105 double totalWork,
106 boolean cancelled ) {
107 this(activityName, message, computePercentage(workedSoFar, totalWork), cancelled);
108 }
109
110 /**
111 * Get the name of the activity.
112 *
113 * @return the activity's name
114 */
115 public String getActivityName() {
116 return this.activityName;
117 }
118
119 /**
120 * Get the progress as a percentage of the total work that's been completed.
121 *
122 * @return the percentage worked, ranging from 0.0 to 100.0
123 */
124 public double getPercentWorked() {
125 return this.percentWorked;
126 }
127
128 /**
129 * Get the progress monitor's text message.
130 *
131 * @return the text message
132 */
133 public String getMessage() {
134 return this.message;
135 }
136
137 /**
138 * Return whether work on this activity has completed.
139 *
140 * @return true if work has completed, or false if work on the activity is still progressing
141 * @see #isCancelled()
142 */
143 public boolean isDone() {
144 return done;
145 }
146
147 /**
148 * Return whether the activity was requested to be cancelled.
149 *
150 * @return cancelled
151 * @see #isDone()
152 */
153 public boolean isCancelled() {
154 return this.cancelled;
155 }
156
157 /**
158 * {@inheritDoc}
159 */
160 @Override
161 public String toString() {
162 String percentage = new DecimalFormat(PERCENTAGE_PATTERN).format(getPercentWorked());
163 percentage = StringUtil.justifyRight(percentage, PERCENTAGE_PATTERN.length(), ' ');
164 String cancelled = this.isCancelled() ? " (cancelled)" : "";
165 return this.activityName + " (" + this.message + ") " + percentage + " %" + cancelled;
166 }
167
168 /**
169 * {@inheritDoc}
170 */
171 @Override
172 public int hashCode() {
173 return this.getActivityName().hashCode();
174 }
175
176 /**
177 * {@inheritDoc}
178 */
179 @Override
180 public boolean equals( Object obj ) {
181 if (this == obj) return true;
182 if (obj instanceof ProgressStatus) {
183 final ProgressStatus that = (ProgressStatus)obj;
184 // First check that the name is the same ...
185 if (!this.getActivityName().equals(that.getActivityName())) return false;
186 // Then check doneness and percent complete ...
187 if (this.isDone() != that.isDone()) return false;
188 if (!isSamePercentage(this.getPercentWorked(), that.getPercentWorked())) return false;
189 return true;
190 }
191 return false;
192 }
193
194 /**
195 * {@inheritDoc}
196 */
197 public int compareTo( ProgressStatus that ) {
198 if (this == that) return 0;
199
200 // First check the name ...
201 int diff = this.getActivityName().compareTo(that.getActivityName());
202 if (diff != 0) return diff;
203
204 // Then check the percentage ...
205 return Double.compare(this.getPercentWorked(), that.getPercentWorked());
206 }
207 }