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