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    package org.jboss.dna.common.math;
023    
024    import java.math.BigDecimal;
025    import java.text.DecimalFormat;
026    import java.util.concurrent.TimeUnit;
027    
028    /**
029     * A number representing an immutable duration of time. This is intended to be used in the same manner as other {@link Number}
030     * subclasses.
031     */
032    public class Duration extends Number implements Comparable<Duration> {
033    
034        private static final long serialVersionUID = 1L;
035    
036        private final long durationInNanos;
037        private Components components;
038    
039        /**
040         * Create a duration given the number of nanoseconds.
041         * 
042         * @param nanos the number of nanoseconds in the duration
043         */
044        public Duration( long nanos ) {
045            this(nanos, TimeUnit.NANOSECONDS);
046        }
047    
048        /**
049         * Create a duration and the time unit.
050         * 
051         * @param duration the duration in the supplied time units
052         * @param unit the time unit
053         */
054        public Duration( long duration,
055                         TimeUnit unit ) {
056            this.durationInNanos = TimeUnit.NANOSECONDS.convert(duration, unit);
057        }
058    
059        /**
060         * {@inheritDoc}
061         */
062        @Override
063        public double doubleValue() {
064            return this.durationInNanos;
065        }
066    
067        /**
068         * {@inheritDoc}
069         */
070        @Override
071        public float floatValue() {
072            return this.durationInNanos;
073        }
074    
075        /**
076         * {@inheritDoc}
077         */
078        @Override
079        public int intValue() {
080            return (int)this.durationInNanos;
081        }
082    
083        /**
084         * {@inheritDoc}
085         */
086        @Override
087        public long longValue() {
088            return this.durationInNanos;
089        }
090    
091        public BigDecimal toBigDecimal() {
092            return new BigDecimal(this.durationInNanos);
093        }
094    
095        /**
096         * Add the supplied duration to this duration, and return the result.
097         * 
098         * @param duration the duration to add to this object
099         * @param unit the unit of the duration being added; may not be null
100         * @return the total duration
101         */
102        public Duration add( long duration,
103                             TimeUnit unit ) {
104            long durationInNanos = TimeUnit.NANOSECONDS.convert(duration, unit);
105            return new Duration(this.durationInNanos + durationInNanos);
106        }
107    
108        /**
109         * Subtract the supplied duration from this duration, and return the result.
110         * 
111         * @param duration the duration to subtract from this object
112         * @param unit the unit of the duration being subtracted; may not be null
113         * @return the total duration
114         */
115        public Duration subtract( long duration,
116                                  TimeUnit unit ) {
117            long durationInNanos = TimeUnit.NANOSECONDS.convert(duration, unit);
118            return new Duration(this.durationInNanos - durationInNanos);
119        }
120    
121        /**
122         * Add the supplied duration to this duration, and return the result. A null value is treated as a duration of 0 nanoseconds.
123         * 
124         * @param duration the duration to add to this object
125         * @return the total duration
126         */
127        public Duration add( Duration duration ) {
128            return new Duration(this.durationInNanos + (duration == null ? 0l : duration.longValue()));
129        }
130    
131        /**
132         * Subtract the supplied duration from this duration, and return the result. A null value is treated as a duration of 0
133         * nanoseconds.
134         * 
135         * @param duration the duration to subtract from this object
136         * @return the resulting duration
137         */
138        public Duration subtract( Duration duration ) {
139            return new Duration(this.durationInNanos - (duration == null ? 0l : duration.longValue()));
140        }
141    
142        /**
143         * Multiply the duration by the supplied scale factor, and return the result.
144         * 
145         * @param scale the factor by which the duration is to be scaled.
146         * @return the scaled duration
147         */
148        public Duration multiply( long scale ) {
149            return new Duration(this.durationInNanos * scale);
150        }
151    
152        /**
153         * Divide the duration by the supplied number, and return the result.
154         * 
155         * @param denominator the factor by which the duration is to be divided.
156         * @return the resulting duration
157         */
158        public Duration divide( long denominator ) {
159            return new Duration(this.durationInNanos / denominator);
160        }
161    
162        /**
163         * Divide the duration by another duration to calculate the ratio.
164         * 
165         * @param duration the duration that this duration is to be divided by; may not be null
166         * @return the resulting duration
167         */
168        public double divide( Duration duration ) {
169            return this.toBigDecimal().divide(duration.toBigDecimal()).doubleValue();
170        }
171    
172        /**
173         * {@inheritDoc}
174         */
175        public int compareTo( Duration that ) {
176            if (that == null) return 1;
177            return this.durationInNanos < that.durationInNanos ? -1 : this.durationInNanos > that.durationInNanos ? 1 : 0;
178        }
179    
180        /**
181         * Return the total duration in nanoseconds.
182         * 
183         * @return the total duration in nanoseconds
184         */
185        public long getDuratinInNanoseconds() {
186            return this.durationInNanos;
187        }
188    
189        /**
190         * Return the total duration in microseconds, which may contain a fraction part for the sub-microsecond component.
191         * 
192         * @return the total duration in microseconds
193         */
194        public BigDecimal getDurationInMicroseconds() {
195            return this.toBigDecimal().divide(new BigDecimal(1000));
196        }
197    
198        /**
199         * Return the total duration in microseconds, which may contain a fraction part for the sub-microsecond component.
200         * 
201         * @return the total duration in microseconds
202         */
203        public BigDecimal getDurationInMilliseconds() {
204            return this.toBigDecimal().divide(new BigDecimal(1000000));
205        }
206    
207        /**
208         * Return the total duration in microseconds, which may contain a fraction part for the sub-microsecond component.
209         * 
210         * @return the total duration in microseconds
211         */
212        public BigDecimal getDurationInSeconds() {
213            return this.toBigDecimal().divide(new BigDecimal(1000000000));
214        }
215    
216        /**
217         * Return the duration components.
218         * 
219         * @return the individual time components of this duration
220         */
221        public Components getComponents() {
222            if (this.components == null) {
223                // Calculate how many seconds, and don't lose any information ...
224                BigDecimal bigSeconds = new BigDecimal(this.durationInNanos).divide(new BigDecimal(1000000000));
225                // Calculate the minutes, and round to lose the seconds
226                int minutes = bigSeconds.intValue() / 60;
227                // Remove the minutes from the seconds, to just have the remainder of seconds
228                double dMinutes = minutes;
229                double seconds = bigSeconds.doubleValue() - dMinutes * 60;
230                // Now compute the number of full hours, and change 'minutes' to hold the remainding minutes
231                int hours = minutes / 60;
232                minutes = minutes - (hours * 60);
233                this.components = new Components(hours, minutes, seconds);
234            }
235            return this.components;
236        }
237    
238        /**
239         * Get the duration value in the supplied unit of time.
240         * 
241         * @param unit the unit of time for the returned value; may not be null
242         * @return the value of this duration in the supplied unit of time
243         */
244        public long getDuration( TimeUnit unit ) {
245            if (unit == null) throw new IllegalArgumentException();
246            return unit.convert(durationInNanos, TimeUnit.NANOSECONDS);
247        }
248    
249        /**
250         * Writes the duration in a form containing hours, minutes, and seconds, including the fractional part of the seconds. The
251         * format is essentially <code>HHH:MM:SS.mmm,mmm</code>, where
252         * <dl>
253         * <dt>HHH</dt>
254         * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd>
255         * <dt>MM</dt>
256         * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd>
257         * <dt>SS</dt>
258         * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd>
259         * <dt>mmm,mmm</dt>
260         * <dd>is the fractional part of seconds, written in at least millisecond precision and up to microsecond precision. The comma
261         * appears if more than 3 digits are used.
262         * </dl>
263         * 
264         * @return a string representation of the duration
265         */
266        @Override
267        public String toString() {
268            // Insert a comma after the milliseconds, if there are enough digits ..
269            return this.getComponents().toString().replaceAll("(\\d{2}).(\\d{3})(\\d{1,3})", "$1.$2,$3");
270        }
271    
272        /**
273         * The atomic components of this duration, broken down into whole hours, minutes and (fractional) seconds.
274         */
275        public class Components {
276    
277            private final int hours;
278            private final int minutes;
279            private final double seconds;
280    
281            protected Components( int hours,
282                                  int minutes,
283                                  double seconds ) {
284                this.hours = hours;
285                this.minutes = minutes;
286                this.seconds = seconds;
287            }
288    
289            /**
290             * Get the whole hours in this duration.
291             * 
292             * @return the hours
293             */
294            public int getHours() {
295                return hours;
296            }
297    
298            /**
299             * Get the whole minutes in this duration.
300             * 
301             * @return the minutes, from 0 to 59.
302             */
303            public int getMinutes() {
304                return minutes;
305            }
306    
307            /**
308             * Get the duration's seconds component.
309             * 
310             * @return the number of seconds, including fractional part.
311             */
312            public double getSeconds() {
313                return seconds;
314            }
315    
316            /**
317             * Return the duration as a string in a form containing hours, minutes, and seconds, including the fractional part of the
318             * seconds. The format is essentially <code>HHH:MM:SS.mmm</code>, where
319             * <dl>
320             * <dt>HHH</dt>
321             * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd>
322             * <dt>MM</dt>
323             * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd>
324             * <dt>SS</dt>
325             * <dd>is the number of hours written in at least 2 digits (e.g., "03")</dd>
326             * <dt>mmm</dt>
327             * <dd>is the fractional part of seconds, written with 3-6 digits (any trailing zeros are dropped)
328             * </dl>
329             * 
330             * @return a string representation of the duration components
331             */
332            @Override
333            public String toString() {
334                // Format the string, and have at least 2 digits for the hours, minutes and whole seconds,
335                // and between 3 and 6 digits for the fractional part of the seconds...
336                String result = new DecimalFormat("######00").format(hours) + ':' + new DecimalFormat("00").format(minutes) + ':'
337                                + new DecimalFormat("00.000###").format(seconds);
338                return result;
339            }
340        }
341    
342    }