001    /*
002     * SonarQube, open source software quality management tool.
003     * Copyright (C) 2008-2014 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * SonarQube is free software; you can redistribute it and/or
007     * modify it under the terms of the GNU Lesser General Public
008     * License as published by the Free Software Foundation; either
009     * version 3 of the License, or (at your option) any later version.
010     *
011     * SonarQube is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     * Lesser General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public License
017     * along with this program; if not, write to the Free Software Foundation,
018     * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019     */
020    
021    package org.sonar.api.utils.internal;
022    
023    import com.google.common.annotations.VisibleForTesting;
024    import org.apache.commons.lang.builder.ToStringBuilder;
025    import org.apache.commons.lang.builder.ToStringStyle;
026    
027    import javax.annotation.Nullable;
028    
029    import java.io.Serializable;
030    
031    /**
032     * @since 4.2
033     */
034    public class WorkDuration implements Serializable {
035    
036      static final int DAY_POSITION_IN_LONG = 10000;
037      static final int HOUR_POSITION_IN_LONG = 100;
038      static final int MINUTE_POSITION_IN_LONG = 1;
039    
040      public static enum UNIT {
041        DAYS, HOURS, MINUTES
042      }
043    
044      private int hoursInDay;
045    
046      private long durationInMinutes;
047      private int days;
048      private int hours;
049      private int minutes;
050    
051      private WorkDuration(long durationInMinutes, int days, int hours, int minutes, int hoursInDay) {
052        this.durationInMinutes = durationInMinutes;
053        this.days = days;
054        this.hours = hours;
055        this.minutes = minutes;
056        this.hoursInDay = hoursInDay;
057      }
058    
059      public static WorkDuration create(int days, int hours, int minutes, int hoursInDay) {
060        long durationInSeconds = 60L * days * hoursInDay;
061        durationInSeconds += 60L * hours;
062        durationInSeconds += minutes;
063        return new WorkDuration(durationInSeconds, days, hours, minutes, hoursInDay);
064      }
065    
066      public static WorkDuration createFromValueAndUnit(int value, UNIT unit, int hoursInDay) {
067        switch (unit) {
068          case DAYS:
069            return create(value, 0, 0, hoursInDay);
070          case HOURS:
071            return create(0, value, 0, hoursInDay);
072          case MINUTES:
073            return create(0, 0, value, hoursInDay);
074          default:
075            throw new IllegalStateException("Cannot create work duration");
076        }
077      }
078    
079      static WorkDuration createFromLong(long duration, int hoursInDay) {
080        int days = 0, hours = 0, minutes = 0;
081    
082        long time = duration;
083        Long currentTime = time / WorkDuration.DAY_POSITION_IN_LONG;
084        if (currentTime > 0) {
085          days = (currentTime.intValue());
086          time = time - (currentTime * WorkDuration.DAY_POSITION_IN_LONG);
087        }
088    
089        currentTime = time / WorkDuration.HOUR_POSITION_IN_LONG;
090        if (currentTime > 0) {
091          hours = currentTime.intValue();
092          time = time - (currentTime * WorkDuration.HOUR_POSITION_IN_LONG);
093        }
094    
095        currentTime = time / WorkDuration.MINUTE_POSITION_IN_LONG;
096        if (currentTime > 0) {
097          minutes = currentTime.intValue();
098        }
099        return WorkDuration.create(days, hours, minutes, hoursInDay);
100      }
101    
102      static WorkDuration createFromMinutes(long duration, int hoursInDay) {
103        int days = ((Double) ((double) duration / hoursInDay / 60d)).intValue();
104        Long currentDurationInMinutes = duration - (60L * days * hoursInDay);
105        int hours = ((Double) (currentDurationInMinutes / 60d)).intValue();
106        currentDurationInMinutes = currentDurationInMinutes - (60L * hours);
107        return new WorkDuration(duration, days, hours, currentDurationInMinutes.intValue(), hoursInDay);
108      }
109    
110      /**
111       * Return the duration in number of working days.
112       * For instance, 3 days and 4 hours will return 3.5 days (if hoursIndDay is 8).
113       */
114      public double toWorkingDays() {
115        return durationInMinutes / 60d / hoursInDay;
116      }
117    
118      /**
119       * Return the duration using the following format DDHHMM, where DD is the number of days, HH is the number of months, and MM the number of minutes.
120       * For instance, 3 days and 4 hours will return 030400 (if hoursIndDay is 8).
121       */
122      public long toLong() {
123        int workingDays = days;
124        int workingHours = hours;
125        if (hours >= hoursInDay) {
126          int nbAdditionalDays = hours / hoursInDay;
127          workingDays += nbAdditionalDays;
128          workingHours = hours - (nbAdditionalDays * hoursInDay);
129        }
130        return workingDays * DAY_POSITION_IN_LONG + workingHours * HOUR_POSITION_IN_LONG + minutes * MINUTE_POSITION_IN_LONG;
131      }
132    
133      public long toMinutes() {
134        return durationInMinutes;
135      }
136    
137      public WorkDuration add(@Nullable WorkDuration with) {
138        if (with != null) {
139          return WorkDuration.createFromMinutes(this.toMinutes() + with.toMinutes(), this.hoursInDay);
140        } else {
141          return this;
142        }
143      }
144    
145      public WorkDuration subtract(@Nullable WorkDuration with) {
146        if (with != null) {
147          return WorkDuration.createFromMinutes(this.toMinutes() - with.toMinutes(), this.hoursInDay);
148        } else {
149          return this;
150        }
151      }
152    
153      public WorkDuration multiply(int factor) {
154        return WorkDuration.createFromMinutes(this.toMinutes() * factor, this.hoursInDay);
155      }
156    
157      public int days() {
158        return days;
159      }
160    
161      public int hours() {
162        return hours;
163      }
164    
165      public int minutes() {
166        return minutes;
167      }
168    
169      @VisibleForTesting
170      int hoursInDay() {
171        return hoursInDay;
172      }
173    
174      @Override
175      public boolean equals(Object o) {
176        if (this == o) {
177          return true;
178        }
179        if (o == null || getClass() != o.getClass()) {
180          return false;
181        }
182    
183        WorkDuration that = (WorkDuration) o;
184        if (durationInMinutes != that.durationInMinutes) {
185          return false;
186        }
187    
188        return true;
189      }
190    
191      @Override
192      public int hashCode() {
193        return (int) (durationInMinutes ^ (durationInMinutes >>> 32));
194      }
195    
196      @Override
197      public String toString() {
198        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
199      }
200    }