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