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; 022 023 import org.apache.commons.lang.StringUtils; 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 import java.util.regex.Matcher; 031 import java.util.regex.Pattern; 032 033 /** 034 * @since 4.3 035 */ 036 public class Duration implements Serializable { 037 038 public static final String DAY = "d"; 039 public static final String HOUR = "h"; 040 public static final String MINUTE = "min"; 041 042 private static final short MINUTES_IN_ONE_HOUR = 60; 043 044 private final long durationInMinutes; 045 046 private Duration(long durationInMinutes) { 047 this.durationInMinutes = durationInMinutes; 048 } 049 050 private Duration(int days, int hours, int minutes, int hoursInDay) { 051 this(((long) days * hoursInDay * MINUTES_IN_ONE_HOUR) + (hours * MINUTES_IN_ONE_HOUR) + minutes); 052 } 053 054 /** 055 * Create a Duration from a number of minutes. 056 */ 057 public static Duration create(long durationInMinutes) { 058 return new Duration(durationInMinutes); 059 } 060 061 /** 062 * Create a Duration from a text duration and the number of hours in a day. 063 * <br> 064 * For instance, Duration.decode("1d 1h", 8) will have a number of minutes of 540 (1*8*60 + 60). 065 * */ 066 public static Duration decode(String text, int hoursInDay) { 067 int days = 0, hours = 0, minutes = 0; 068 String sanitizedText = StringUtils.deleteWhitespace(text); 069 Pattern pattern = Pattern.compile("\\s*+(?:(\\d++)\\s*+" + DAY + ")?+\\s*+(?:(\\d++)\\s*+" + HOUR + ")?+\\s*+(?:(\\d++)\\s*+" + MINUTE + ")?+\\s*+"); 070 Matcher matcher = pattern.matcher(text); 071 072 try { 073 if (matcher.find()) { 074 String daysDuration = matcher.group(1); 075 if (daysDuration != null) { 076 days = Integer.parseInt(daysDuration); 077 sanitizedText = sanitizedText.replace(daysDuration + DAY, ""); 078 } 079 String hoursText = matcher.group(2); 080 if (hoursText != null) { 081 hours = Integer.parseInt(hoursText); 082 sanitizedText = sanitizedText.replace(hoursText + HOUR, ""); 083 } 084 String minutesText = matcher.group(3); 085 if (minutesText != null) { 086 minutes = Integer.parseInt(minutesText); 087 sanitizedText = sanitizedText.replace(minutesText + MINUTE, ""); 088 } 089 if (sanitizedText.isEmpty()) { 090 return new Duration(days, hours, minutes, hoursInDay); 091 } 092 } 093 throw invalid(text, null); 094 } catch (NumberFormatException e) { 095 throw invalid(text, e); 096 } 097 } 098 099 /** 100 * Return the duration in text, by using the given hours in day parameter to process hours. 101 * <br/> 102 * Duration.decode("1d 1h", 8).encode(8) will return "1d 1h" 103 * Duration.decode("2d", 8).encode(16) will return "1d" 104 */ 105 public String encode(int hoursInDay) { 106 int days = ((Double) ((double) durationInMinutes / hoursInDay / MINUTES_IN_ONE_HOUR)).intValue(); 107 Long remainingDuration = durationInMinutes - (days * hoursInDay * MINUTES_IN_ONE_HOUR); 108 int hours = ((Double) (remainingDuration.doubleValue() / MINUTES_IN_ONE_HOUR)).intValue(); 109 remainingDuration = remainingDuration - (hours * MINUTES_IN_ONE_HOUR); 110 int minutes = remainingDuration.intValue(); 111 112 StringBuilder stringBuilder = new StringBuilder(); 113 if (days > 0) { 114 stringBuilder.append(days); 115 stringBuilder.append(DAY); 116 } 117 if (hours > 0) { 118 stringBuilder.append(hours); 119 stringBuilder.append(HOUR); 120 } 121 if (minutes > 0) { 122 stringBuilder.append(minutes); 123 stringBuilder.append(MINUTE); 124 } 125 return stringBuilder.toString(); 126 } 127 128 /** 129 * Return the duration in minutes. 130 * <br> 131 * For instance, Duration.decode(1h, 24).toMinutes() will return 60. 132 */ 133 public long toMinutes() { 134 return durationInMinutes; 135 } 136 137 /** 138 * Return true if the given duration is greater than the current one. 139 */ 140 public boolean isGreaterThan(Duration other) { 141 return toMinutes() > other.toMinutes(); 142 } 143 144 /** 145 * Add the given duration to the current one. 146 */ 147 public Duration add(Duration with) { 148 return Duration.create(durationInMinutes + with.durationInMinutes); 149 } 150 151 /** 152 * Subtract the given duration to the current one. 153 */ 154 public Duration subtract(Duration with) { 155 return Duration.create(durationInMinutes - with.durationInMinutes); 156 } 157 158 /** 159 * Multiply the duration with the given factor. 160 */ 161 public Duration multiply(int factor) { 162 return Duration.create(durationInMinutes * factor); 163 } 164 165 private static IllegalArgumentException invalid(String text, @Nullable Exception e) { 166 throw new IllegalArgumentException(String.format("Duration '%s' is invalid, it should use the following sample format : 2d 10h 15min", text), e); 167 } 168 169 @Override 170 public boolean equals(Object o) { 171 if (this == o) { 172 return true; 173 } 174 if (o == null || getClass() != o.getClass()) { 175 return false; 176 } 177 178 Duration that = (Duration) o; 179 if (durationInMinutes != that.durationInMinutes) { 180 return false; 181 } 182 183 return true; 184 } 185 186 @Override 187 public int hashCode() { 188 return (int) (durationInMinutes ^ (durationInMinutes >>> 32)); 189 } 190 191 @Override 192 public String toString() { 193 return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); 194 } 195 }