001/**
002 * Copyright 2010-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.common.util.inform;
017
018import org.kuali.common.util.Assert;
019
020/**
021 * Print a dot to the console each time we make at least 1% progress towards a total
022 */
023public final class PercentCompleteInformer {
024
025        public static final int DEFAULT_PERCENTAGE_INCREMENT = 1;
026        public static final long UNINITIALIZED_PROGRESS_INDICATOR = -1;
027        public static final int UNINITIALIZED_PERCENT_COMPLETE_INDICATOR = -1;
028
029        private final int percentageIncrement;
030        private final long total;
031        private final Inform inform;
032        private final StartStopInformer informer;
033
034        public PercentCompleteInformer(long total) {
035                this(total, DEFAULT_PERCENTAGE_INCREMENT);
036        }
037
038        public PercentCompleteInformer(long total, int percentageIncrement) {
039                this(total, DEFAULT_PERCENTAGE_INCREMENT, Inform.DEFAULT_INFORM);
040        }
041
042        public PercentCompleteInformer(long total, int percentageIncrement, Inform inform) {
043                Assert.isTrue(total >= 0, "total is negative");
044                Assert.isTrue(percentageIncrement > 0, "percentage increment must be greater than zero");
045                Assert.noNulls(inform);
046                this.total = total;
047                this.inform = inform;
048                this.percentageIncrement = percentageIncrement;
049                this.informer = new StartStopInformer(inform);
050        }
051
052        private int percentComplete = UNINITIALIZED_PERCENT_COMPLETE_INDICATOR;
053        private volatile long progress = UNINITIALIZED_PROGRESS_INDICATOR;
054        private boolean started = false;
055
056        /**
057         * Indicates if we are in the "started" state or not
058         */
059        public boolean isStarted() {
060                return started;
061        }
062
063        /**
064         * Indicates how far along we are
065         */
066        public long getProgress() {
067                return progress;
068        }
069
070        /**
071         * Indicates how far along we are as a percentage
072         */
073        public int getPercentComplete() {
074                return percentComplete;
075        }
076
077        /**
078         * Thread safe method to indicate progress has begun
079         */
080        public synchronized void start() {
081                Assert.isFalse(started, "Already started");
082                this.started = true;
083                this.percentComplete = 0;
084                this.progress = 0;
085                informer.start();
086        }
087
088        /**
089         * Thread safe method for incrementing progress by one
090         */
091        public void incrementProgress() {
092                incrementProgress(1);
093        }
094
095        /**
096         * Thread safe method for incrementing progress by <code>amount</code>
097         */
098        public synchronized void incrementProgress(long amount) {
099                // Make sure were are in the started state
100                Assert.isTrue(started, "Not started");
101
102                // Increment the progress indicator
103                this.progress += amount;
104
105                // Calculate how far along we are
106                int newPercentCompleted = (int) ((progress * 100) / total);
107
108                // Have we made at least 1% progress?
109                if (isEnoughProgress(newPercentCompleted, percentComplete, percentageIncrement)) {
110                        // If so, update the field holding the percent complete
111                        this.percentComplete = newPercentCompleted;
112                        // and print a dot to the console
113                        inform.getPrintStream().print(inform.getProgressToken());
114                }
115        }
116
117        /**
118         * Thread safe method to indicate progress has stopped
119         */
120        public synchronized void stop() {
121                Assert.isTrue(started, "Not started");
122                this.started = false;
123                this.percentComplete = UNINITIALIZED_PERCENT_COMPLETE_INDICATOR;
124                this.progress = UNINITIALIZED_PROGRESS_INDICATOR;
125                informer.stop();
126        }
127
128        protected boolean isEnoughProgress(int newPercentCompleted, int percentComplete, int percentageIncrement) {
129                int needed = percentComplete + percentageIncrement;
130                return newPercentCompleted >= needed;
131        }
132
133        public int getPercentageIncrement() {
134                return percentageIncrement;
135        }
136
137        public long getTotal() {
138                return total;
139        }
140
141        public Inform getInform() {
142                return inform;
143        }
144
145        public StartStopInformer getInformer() {
146                return informer;
147        }
148
149}