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    }