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
021package org.sonar.api.utils.internal;
022
023import com.google.common.annotations.VisibleForTesting;
024import org.apache.commons.lang.builder.ToStringBuilder;
025import org.apache.commons.lang.builder.ToStringStyle;
026
027import javax.annotation.Nullable;
028
029import java.io.Serializable;
030
031/**
032 * @since 4.2
033 */
034public 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 = (int)(duration / (double)hoursInDay / 60.0);
104    Long currentDurationInMinutes = duration - (60L * days * hoursInDay);
105    int hours = (int)(currentDurationInMinutes / 60.0);
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 1L * 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}