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 }