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