001/* 002 * SonarQube 003 * Copyright (C) 2009-2016 SonarSource SA 004 * mailto:contact AT sonarsource DOT com 005 * 006 * This program 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 * This program 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 */ 020package org.sonar.api.utils; 021 022import java.util.Locale; 023import javax.annotation.CheckForNull; 024import org.sonar.api.CoreProperties; 025import org.sonar.api.batch.BatchSide; 026import org.sonar.api.config.Settings; 027import org.sonar.api.i18n.I18n; 028import org.sonar.api.server.ServerSide; 029 030/** 031 * Used through ruby code <pre>Internal.durations</pre> 032 * 033 * @since 4.3 034 */ 035@BatchSide 036@ServerSide 037public class Durations { 038 039 public enum DurationFormat { 040 /** 041 * Display duration with only one or two members. 042 * For instance, Duration.decode("1d 1h 10min", 8) will return "1d 1h" and Duration.decode("12d 5h", 8) will return "12d" 043 */ 044 SHORT 045 } 046 047 private final Settings settings; 048 private final I18n i18n; 049 050 public Durations(Settings settings, I18n i18n) { 051 this.settings = settings; 052 this.i18n = i18n; 053 } 054 055 /** 056 * Create a Duration object from a number of minutes 057 */ 058 public Duration create(long minutes) { 059 return Duration.create(minutes); 060 } 061 062 /** 063 * Convert the text to a Duration 064 * <br> 065 * Example : decode("9d 10 h") -> Duration.encode("10d2h") (if sonar.technicalDebt.hoursInDay property is set to 8)<br /> 066 * Throws {@code IllegalArgumentException} 067 */ 068 public Duration decode(String duration) { 069 return Duration.decode(duration, hoursInDay()); 070 } 071 072 /** 073 * Return the string value of the Duration. 074 * <br> 075 * Example : encode(Duration.encode("9d 10h")) -> "10d2h" (if sonar.technicalDebt.hoursInDay property is set to 8) 076 */ 077 public String encode(Duration duration) { 078 return duration.encode(hoursInDay()); 079 } 080 081 /** 082 * Return the formatted work duration. 083 * <br> 084 * Example : format(Locale.FRENCH, Duration.encode("9d 10h"), DurationFormat.SHORT) -> 10j 2h (if sonar.technicalDebt.hoursInDay property is set to 8) 085 * 086 */ 087 public String format(Locale locale, Duration duration, DurationFormat format) { 088 return format(locale, duration); 089 } 090 091 /** 092 * Return the formatted work duration. 093 * <br> 094 * Example : format(Locale.FRENCH, Duration.encode("9d 10h"), DurationFormat.SHORT) -> 10j 2h (if sonar.technicalDebt.hoursInDay property is set to 8) 095 * 096 */ 097 public String format(Locale locale, Duration duration) { 098 Long durationInMinutes = duration.toMinutes(); 099 if (durationInMinutes == 0) { 100 return "0"; 101 } 102 boolean isNegative = durationInMinutes < 0; 103 Long absDuration = Math.abs(durationInMinutes); 104 105 int days = ((Double) ((double) absDuration / hoursInDay() / 60)).intValue(); 106 Long remainingDuration = absDuration - (days * hoursInDay() * 60); 107 int hours = ((Double) (remainingDuration.doubleValue() / 60)).intValue(); 108 remainingDuration = remainingDuration - (hours * 60); 109 int minutes = remainingDuration.intValue(); 110 111 return format(locale, days, hours, minutes, isNegative); 112 } 113 114 private String format(Locale locale, int days, int hours, int minutes, boolean isNegative) { 115 StringBuilder message = new StringBuilder(); 116 if (days > 0) { 117 message.append(message(locale, "work_duration.x_days", isNegative ? (-1 * days) : days)); 118 } 119 if (displayHours(days, hours)) { 120 addSpaceIfNeeded(message); 121 message.append(message(locale, "work_duration.x_hours", isNegative && message.length() == 0 ? (-1 * hours) : hours)); 122 } 123 if (displayMinutes(days, hours, minutes)) { 124 addSpaceIfNeeded(message); 125 message.append(message(locale, "work_duration.x_minutes", isNegative && message.length() == 0 ? (-1 * minutes) : minutes)); 126 } 127 return message.toString(); 128 } 129 130 private String message(Locale locale, String key, @CheckForNull Object parameter) { 131 return i18n.message(locale, key, null, parameter); 132 } 133 134 private static boolean displayHours(int days, int hours) { 135 return hours > 0 && days < 10; 136 } 137 138 private static boolean displayMinutes(int days, int hours, int minutes) { 139 return minutes > 0 && hours < 10 && days == 0; 140 } 141 142 private static void addSpaceIfNeeded(StringBuilder message) { 143 if (message.length() > 0) { 144 message.append(" "); 145 } 146 } 147 148 private int hoursInDay() { 149 return settings.getInt(CoreProperties.HOURS_IN_DAY); 150 } 151 152}