001    /*
002     * SonarQube, open source software quality management tool.
003     * Copyright (C) 2008-2013 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;
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 durationInSeconds;
047      private int days;
048      private int hours;
049      private int minutes;
050    
051      private WorkDuration(long durationInSeconds, int days, int hours, int minutes, int hoursInDay) {
052        this.durationInSeconds = durationInSeconds;
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 = 3600L * days * hoursInDay;
061        durationInSeconds += 3600L * hours;
062        durationInSeconds += 60L * 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 createFromSeconds(long seconds, int hoursInDay) {
103        int days = (int) (seconds / hoursInDay / 60f / 60f);
104        long currentDurationInSeconds = seconds - (3600L * days * hoursInDay);
105        int hours = (int) (currentDurationInSeconds / 60f / 60f);
106        currentDurationInSeconds = currentDurationInSeconds - (3600L * hours);
107        int minutes = (int) (currentDurationInSeconds / 60f);
108        return new WorkDuration(seconds, days, hours, minutes, hoursInDay);
109      }
110    
111      /**
112       * Return the duration in number of working days.
113       * For instance, 3 days and 4 hours will return 3.5 days (if hoursIndDay is 8).
114       */
115      public double toWorkingDays() {
116        return durationInSeconds / 60d / 60d / hoursInDay;
117      }
118    
119      /**
120       * 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.
121       * For instance, 3 days and 4 hours will return 030400 (if hoursIndDay is 8).
122       */
123      public long toLong() {
124        int workingDays = days;
125        int workingHours = hours;
126        if (hours >= hoursInDay) {
127          int nbAdditionalDays = hours / hoursInDay;
128          workingDays += nbAdditionalDays;
129          workingHours = hours - (nbAdditionalDays * hoursInDay);
130        }
131        return workingDays * DAY_POSITION_IN_LONG + workingHours * HOUR_POSITION_IN_LONG + minutes * MINUTE_POSITION_IN_LONG;
132      }
133    
134      public long toSeconds() {
135        return durationInSeconds;
136      }
137    
138      public WorkDuration add(@Nullable WorkDuration with) {
139        if (with != null) {
140          return WorkDuration.createFromSeconds(this.toSeconds() + with.toSeconds(), this.hoursInDay);
141        } else {
142          return this;
143        }
144      }
145    
146      public WorkDuration subtract(@Nullable WorkDuration with) {
147        if (with != null) {
148          return WorkDuration.createFromSeconds(this.toSeconds() - with.toSeconds(), this.hoursInDay);
149        } else {
150          return this;
151        }
152      }
153    
154      public WorkDuration multiply(int factor) {
155        return WorkDuration.createFromSeconds(this.toSeconds() * factor, this.hoursInDay);
156      }
157    
158      public int days() {
159        return days;
160      }
161    
162      public int hours() {
163        return hours;
164      }
165    
166      public int minutes() {
167        return minutes;
168      }
169    
170      @VisibleForTesting
171      int hoursInDay() {
172        return hoursInDay;
173      }
174    
175      @Override
176      public boolean equals(Object o) {
177        if (this == o) {
178          return true;
179        }
180        if (o == null || getClass() != o.getClass()) {
181          return false;
182        }
183    
184        WorkDuration that = (WorkDuration) o;
185        if (durationInSeconds != that.durationInSeconds) {
186          return false;
187        }
188    
189        return true;
190      }
191    
192      @Override
193      public int hashCode() {
194        return (int) (durationInSeconds ^ (durationInSeconds >>> 32));
195      }
196    
197      @Override
198      public String toString() {
199        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
200      }
201    }