since_previous_version_detailed=since previous version ({0} - {1})
since_previous_version_detailed.short=\u0394 version ({0})
time_changes=Time changes
-work_duration.x_days={0} days
-work_duration.x_days.short={0}d
-work_duration.x_hours={0} hours
-work_duration.x_hours.short={0}h
-work_duration.x_minutes={0} minutes
-work_duration.x_minutes.short={0}min
+work_duration.x_days={0}d
+work_duration.x_hours={0}h
+work_duration.x_minutes={0}min
#------------------------------------------------------------------------------
#
<br/>
<span style="font-weight: bold">
<%= message('widget.rules.removed') -%>
- <span class="varb"><%= Internal.work_duration_formatter.format(estimated_cleared_technical_debt, 'SHORT') -%></span>
+ <span class="varb"><%= Internal.i18n.formatWorkDuration(estimated_cleared_technical_debt) -%></span>
</span>
<% end %>
<% end %>
</a>
</td>
<td class="val value-debt-<%= h(characteristic.key) -%>">
- <a href="<%= drilldown_url -%>" class="link-debt-<%= h(characteristic.key) -%>"><%= Internal.work_duration_formatter.format(value.to_i, 'SHORT') -%></a>
+ <a href="<%= drilldown_url -%>" class="link-debt-<%= h(characteristic.key) -%>"><%= Internal.i18n.formatWorkDuration(value.to_i) -%></a>
<% if should_display_diff_measures %>
<% if diff_by_characteristic_id[characteristic.id] %>
<%= format_variation(measure) -%>
<% end %>
</td>
<td class="val value-total-<%= h(characteristic.key) -%>">
- <%= Internal.work_duration_formatter.format(cumulated.to_i, 'SHORT') -%>
+ <%= Internal.i18n.formatWorkDuration(cumulated.to_i) -%>
<%
if should_display_diff_measures
css_style = 'var'
css_style += 'b' if total_diff < 0
css_style += 'w' if total_diff > 0
- diff_to_display = (total_diff < 0 ? '' : '+') + Internal.work_duration_formatter.format(total_diff.to_i, 'SHORT')
+ diff_to_display = (total_diff < 0 ? '' : '+') + Internal.i18n.formatWorkDuration(total_diff.to_i)
%>
<span class="<%= css_style -%>"><b>(<%= diff_to_display -%>)</b></span>
<%
import org.sonar.api.platform.PluginMetadata;
import org.sonar.api.utils.HttpDownloader;
import org.sonar.api.utils.UriReader;
+import org.sonar.api.utils.WorkDurationFactory;
import org.sonar.api.utils.internal.TempFolderCleaner;
import org.sonar.batch.components.*;
import org.sonar.core.config.Logback;
import org.sonar.core.i18n.DefaultI18n;
import org.sonar.core.i18n.RuleI18nManager;
+import org.sonar.core.i18n.WorkDurationFormatter;
import org.sonar.core.metric.CacheMetricFinder;
import org.sonar.core.persistence.*;
import org.sonar.core.purge.PurgeProfiler;
PastSnapshotFinderByVersion.class,
PastSnapshotFinderByPreviousVersion.class,
PastMeasuresLoader.class,
- PastSnapshotFinder.class);
+ PastSnapshotFinder.class,
+ WorkDurationFormatter.class,
+ WorkDurationFactory.class
+ );
}
@Override
import java.util.*;
public class DefaultI18n implements I18n, ServerExtension, BatchExtension, Startable {
+
private static final Logger LOG = LoggerFactory.getLogger(DefaultI18n.class);
public static final String BUNDLE_PACKAGE = "org.sonar.l10n.";
+ private final WorkDurationFormatter workDurationFormatter;
private PluginRepository pluginRepository;
private I18nClassloader i18nClassloader;
private final ResourceBundle.Control control;
private final System2 system2;
- public DefaultI18n(PluginRepository pluginRepository) {
- this(pluginRepository, System2.INSTANCE);
+ public DefaultI18n(PluginRepository pluginRepository, WorkDurationFormatter workDurationFormatter) {
+ this(pluginRepository, workDurationFormatter, System2.INSTANCE);
}
@VisibleForTesting
- DefaultI18n(PluginRepository pluginRepository, System2 system2) {
+ DefaultI18n(PluginRepository pluginRepository, WorkDurationFormatter workDurationFormatter, System2 system2) {
this.pluginRepository = pluginRepository;
+ this.workDurationFormatter = workDurationFormatter;
this.system2 = system2;
// SONAR-2927
this.control = new ResourceBundle.Control() {
return DateFormat.getDateInstance(DateFormat.DEFAULT, locale).format(date);
}
+ @Override
+ public String formatWorkDuration(Locale locale, long duration) {
+ if (duration == 0) {
+ return "0";
+ }
+ List<WorkDurationFormatter.Result> results = workDurationFormatter.format(duration);
+ StringBuilder message = new StringBuilder();
+ for (WorkDurationFormatter.Result result : results) {
+ if (result.key().equals(" ")) {
+ message.append(" ");
+ } else {
+ message.append(message(locale, result.key(), null, result.value()));
+ }
+ }
+ return message.toString();
+ }
+
/**
* Only the given locale is searched. Contrary to java.util.ResourceBundle, no strategy for locating the bundle is implemented in
* this method.
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.core.i18n;
+
+import org.apache.commons.lang.builder.ReflectionToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+import org.sonar.api.BatchExtension;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.utils.WorkDuration;
+import org.sonar.api.utils.WorkDurationFactory;
+
+import javax.annotation.CheckForNull;
+
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+public class WorkDurationFormatter implements ServerComponent, BatchExtension {
+
+ private final WorkDurationFactory workDurationFactory;
+
+ public WorkDurationFormatter(WorkDurationFactory workDurationFactory) {
+ this.workDurationFactory = workDurationFactory;
+ }
+
+ public List<Result> format(long durationInSeconds) {
+ List<Result> results = newArrayList();
+ if (durationInSeconds == 0) {
+ return newArrayList(new Result("0", null));
+ }
+ boolean isNegative = durationInSeconds < 0;
+ Long absDuration = Math.abs(durationInSeconds);
+ WorkDuration workDuration = workDurationFactory.createFromMinutes(absDuration);
+ if (workDuration.days() > 0) {
+ results.add(message("work_duration.x_days", isNegative ? -1 * workDuration.days() : workDuration.days()));
+ }
+ if (workDuration.hours() > 0 && workDuration.days() < 10) {
+ if (!results.isEmpty()) {
+ results.add(new Result(" ", null));
+ }
+ results.add(message("work_duration.x_hours", isNegative && results.isEmpty() ? -1 * workDuration.hours() : workDuration.hours()));
+ }
+ if (workDuration.minutes() > 0 && workDuration.hours() < 10 && workDuration.days() == 0) {
+ if (!results.isEmpty()) {
+ results.add(new Result(" ", null));
+ }
+ results.add(message("work_duration.x_minutes", isNegative && results.isEmpty() ? -1 * workDuration.minutes() : workDuration.minutes()));
+ }
+ return results;
+ }
+
+ private Result message(String key, @CheckForNull Object parameter) {
+ return new Result(key, parameter);
+ }
+
+ static class Result {
+ private String key;
+ private Object value;
+
+ Result(String key, Object value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ String key() {
+ return key;
+ }
+
+ Object value() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Result result = (Result) o;
+
+ if (key != null ? !key.equals(result.key) : result.key != null) {
+ return false;
+ }
+ if (value != null ? !value.equals(result.value) : result.value != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = key != null ? key.hashCode() : 0;
+ result = 31 * result + (value != null ? value.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return new ReflectionToStringBuilder(this, ToStringStyle.SIMPLE_STYLE).toString();
+ }
+ }
+}
import java.util.List;
import java.util.Locale;
+import static com.google.common.collect.Lists.newArrayList;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@Mock
System2 system2;
+ @Mock
+ WorkDurationFormatter workDurationFormatter;
+
+
DefaultI18n manager;
@Before
I18nClassloader i18nClassloader = new I18nClassloader(new ClassLoader[]{
newCoreClassloader(), newFrenchPackClassloader(), newSqaleClassloader(), newCheckstyleClassloader()
});
- manager = new DefaultI18n(pluginRepository, system2);
+ manager = new DefaultI18n(pluginRepository, workDurationFormatter, system2);
manager.doStart(i18nClassloader);
}
assertThat(manager.formatDate(Locale.ENGLISH, DateUtils.parseDate("2014-01-22"))).isEqualTo("Jan 22, 2014");
}
+ @Test
+ public void format_work_duration() {
+ when(workDurationFormatter.format(10)).thenReturn(newArrayList(
+ new WorkDurationFormatter.Result("work_duration.x_days", 5),
+ new WorkDurationFormatter.Result(" ", null),
+ new WorkDurationFormatter.Result("work_duration.x_hours", 2),
+ new WorkDurationFormatter.Result(" ", null),
+ new WorkDurationFormatter.Result("work_duration.x_minutes", 1)
+ ));
+ assertThat(manager.formatWorkDuration(Locale.ENGLISH, 10)).isEqualTo("5d 2h 1min");
+ }
+
+ @Test
+ public void format_work_duration_when_0() {
+ assertThat(manager.formatWorkDuration(Locale.ENGLISH, 0)).isEqualTo("0");
+ }
+
static URLClassLoader newCoreClassloader() {
return newClassLoader("/org/sonar/core/i18n/corePlugin/");
}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.core.i18n;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
+import org.sonar.api.utils.WorkDurationFactory;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+
+public class WorkDurationFormatterTest {
+
+ static final int HOURS_IN_DAY = 8;
+
+ static final long ONE_MINUTE = 1L;
+ static final long ONE_HOUR = ONE_MINUTE * 60;
+ static final long ONE_DAY = HOURS_IN_DAY * ONE_HOUR;
+
+ Settings settings;
+
+ WorkDurationFormatter formatter;
+
+ @Before
+ public void setUp() throws Exception {
+ settings = new Settings();
+ settings.setProperty(CoreProperties.HOURS_IN_DAY, Integer.toString(HOURS_IN_DAY));
+ formatter = new WorkDurationFormatter(new WorkDurationFactory(settings));
+ }
+
+ @Test
+ public void format() {
+ assertThat(formatter.format(5 * ONE_DAY)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_days", 5)));
+ assertThat(formatter.format(2 * ONE_HOUR)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_hours", 2)));
+ assertThat(formatter.format(ONE_MINUTE)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_minutes", 1)));
+
+ assertThat(formatter.format(5 * ONE_DAY + 2 * ONE_HOUR)).isEqualTo(newArrayList(
+ new WorkDurationFormatter.Result("work_duration.x_days", 5),
+ new WorkDurationFormatter.Result(" ", null),
+ new WorkDurationFormatter.Result("work_duration.x_hours", 2)
+ ));
+
+ assertThat(formatter.format(2 * ONE_HOUR + ONE_MINUTE)).isEqualTo(newArrayList(
+ new WorkDurationFormatter.Result("work_duration.x_hours", 2),
+ new WorkDurationFormatter.Result(" ", null),
+ new WorkDurationFormatter.Result("work_duration.x_minutes", 1)
+ ));
+
+ assertThat(formatter.format(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE)).isEqualTo(newArrayList(
+ new WorkDurationFormatter.Result("work_duration.x_days", 5),
+ new WorkDurationFormatter.Result(" ", null),
+ new WorkDurationFormatter.Result("work_duration.x_hours", 2)
+ ));
+ }
+
+ @Test
+ public void not_display_following_element_when_bigger_than_ten() {
+ int hoursInDay = 15;
+ settings.setProperty(CoreProperties.HOURS_IN_DAY, Integer.toString(hoursInDay));
+
+ assertThat(formatter.format(15 * hoursInDay * ONE_HOUR + 2 * ONE_HOUR + ONE_MINUTE)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_days", 15)));
+
+ assertThat(formatter.format(12 * ONE_HOUR + ONE_MINUTE)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_hours", 12)));
+ }
+
+ @Test
+ public void display_zero_without_unit() {
+ assertThat(formatter.format(0)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("0", null)));
+ }
+
+ @Test
+ public void display_negative_duration() {
+ assertThat(formatter.format(-5 * ONE_DAY)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_days", -5)));
+ assertThat(formatter.format(-2 * ONE_HOUR)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_hours", -2)));
+ assertThat(formatter.format(-1 * ONE_MINUTE)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_minutes", -1)));
+ }
+
+}
only.in.english=Missing in French bundle
duration.seconds=less than a minute
duration.day=a day
+work_duration.x_days={0}d
+work_duration.x_hours={0}h
+work_duration.x_minutes={0}min
*/
String formatDate(Locale locale, Date date);
+ /**
+ * Return the formatted work duration.
+ * <br>
+ * Example : format(Locale.ENGLISH, WorkDuration.create(10, 2, 0, 8)) -> 10d 2h
+ *
+ * @since 4.3
+ */
+ String formatWorkDuration(Locale locale, long duration);
+
}
package org.sonar.server.issue;
import org.sonar.api.ServerComponent;
+import org.sonar.api.i18n.I18n;
import org.sonar.api.issue.internal.FieldDiffs;
-import org.sonar.core.i18n.DefaultI18n;
import org.sonar.core.issue.IssueUpdater;
-import org.sonar.server.ui.WorkDurationFormatter;
+import org.sonar.server.user.UserSession;
import java.io.Serializable;
import java.util.List;
private static final String ISSUE_CHANGELOG_FIELD = "issue.changelog.field.";
- private final DefaultI18n defaultI18n;
- private final WorkDurationFormatter workDurationFormatter;
+ private final I18n i18n;
- public IssueChangelogFormatter(DefaultI18n defaultI18n, WorkDurationFormatter workDurationFormatter) {
- this.defaultI18n = defaultI18n;
- this.workDurationFormatter = workDurationFormatter;
+ public IssueChangelogFormatter(I18n i18n) {
+ this.i18n = i18n;
}
public List<String> format(Locale locale, FieldDiffs diffs) {
String key = entry.getKey();
IssueChangelogDiffFormat diffFormat = format(locale, key, entry.getValue());
if (diffFormat.newValue() != null) {
- message.append(defaultI18n.message(locale, "issue.changelog.changed_to", null, defaultI18n.message(locale, ISSUE_CHANGELOG_FIELD + key, null), diffFormat.newValue()));
+ message.append(i18n.message(locale, "issue.changelog.changed_to", null, i18n.message(locale, ISSUE_CHANGELOG_FIELD + key, null), diffFormat.newValue()));
} else {
- message.append(defaultI18n.message(locale, "issue.changelog.removed", null, defaultI18n.message(locale, ISSUE_CHANGELOG_FIELD + key, null)));
+ message.append(i18n.message(locale, "issue.changelog.removed", null, i18n.message(locale, ISSUE_CHANGELOG_FIELD + key, null)));
}
if (diffFormat.oldValue() != null) {
message.append(" (");
- message.append(defaultI18n.message(locale, "issue.changelog.was", null, diffFormat.oldValue()));
+ message.append(i18n.message(locale, "issue.changelog.was", null, diffFormat.oldValue()));
message.append(")");
}
result.add(message.toString());
String oldValueString = oldValue != null && !"".equals(oldValue) ? oldValue.toString() : null;
if (IssueUpdater.TECHNICAL_DEBT.equals(key)) {
if (newValueString != null) {
- newValueString = workDurationFormatter.format(Long.parseLong(newValueString), WorkDurationFormatter.Format.SHORT);
+ newValueString = i18n.formatWorkDuration(UserSession.get().locale(), Long.parseLong(newValueString));
}
if (oldValueString != null) {
- oldValueString = workDurationFormatter.format(Long.parseLong(oldValueString), WorkDurationFormatter.Format.SHORT);
+ // TODO use i18n API
+ oldValueString = i18n.formatWorkDuration(UserSession.get().locale(), Long.parseLong(oldValueString));
}
}
return new IssueChangelogDiffFormat(oldValueString, newValueString);
import org.sonar.server.issue.IssueChangelog;
import org.sonar.server.issue.IssueChangelogService;
import org.sonar.server.issue.IssueService;
-import org.sonar.server.ui.WorkDurationFormatter;
import org.sonar.server.user.UserSession;
import javax.annotation.CheckForNull;
private final IssueService issueService;
private final IssueChangelogService issueChangelogService;
private final ActionService actionService;
- private final WorkDurationFormatter workDurationFormatter;
private final DefaultTechnicalDebtManager technicalDebtManager;
private final I18n i18n;
public IssueShowWsHandler(IssueFinder issueFinder, IssueService issueService, IssueChangelogService issueChangelogService, ActionService actionService,
- WorkDurationFormatter workDurationFormatter, DefaultTechnicalDebtManager technicalDebtManager, I18n i18n) {
+ DefaultTechnicalDebtManager technicalDebtManager, I18n i18n) {
this.issueFinder = issueFinder;
this.issueService = issueService;
this.issueChangelogService = issueChangelogService;
this.actionService = actionService;
- this.workDurationFormatter = workDurationFormatter;
this.technicalDebtManager = technicalDebtManager;
this.i18n = i18n;
}
.prop("author", issue.authorLogin())
.prop("actionPlan", actionPlanKey)
.prop("actionPlanName", actionPlan != null ? actionPlan.name() : null)
- .prop("debt", technicalDebt != null ? workDurationFormatter.format(technicalDebt, WorkDurationFormatter.Format.SHORT) : null)
+ .prop("debt", technicalDebt != null ? i18n.formatWorkDuration(UserSession.get().locale(), technicalDebt) : null)
.prop("creationDate", DateUtils.formatDateTime(issue.creationDate()))
.prop("fCreationDate", formatDate(issue.creationDate()))
.prop("updateDate", updateDate != null ? DateUtils.formatDateTime(updateDate) : null)
import org.sonar.core.i18n.DefaultI18n;
import org.sonar.core.i18n.GwtI18n;
import org.sonar.core.i18n.RuleI18nManager;
+import org.sonar.core.i18n.WorkDurationFormatter;
import org.sonar.core.issue.IssueFilterSerializer;
import org.sonar.core.issue.IssueNotifications;
import org.sonar.core.issue.IssueUpdater;
import org.sonar.server.technicaldebt.DebtService;
import org.sonar.server.text.MacroInterpreter;
import org.sonar.server.text.RubyTextService;
-import org.sonar.server.ui.*;
+import org.sonar.server.ui.JRubyI18n;
+import org.sonar.server.ui.JRubyProfiling;
+import org.sonar.server.ui.PageDecorations;
+import org.sonar.server.ui.Views;
import org.sonar.server.user.*;
import org.sonar.server.util.*;
import org.sonar.server.ws.ListingWs;
rootContainer.addSingleton(DefaultI18n.class);
rootContainer.addSingleton(RuleI18nManager.class);
rootContainer.addSingleton(GwtI18n.class);
+ rootContainer.addSingleton(WorkDurationFormatter.class);
+ rootContainer.addSingleton(WorkDurationFactory.class);
+
rootContainer.addSingleton(PreviewDatabaseFactory.class);
rootContainer.addSingleton(SemaphoreUpdater.class);
rootContainer.addSingleton(SemaphoresImpl.class);
servicesContainer.addSingleton(TechnicalDebtModelSynchronizer.class);
servicesContainer.addSingleton(TechnicalDebtModelRepository.class);
servicesContainer.addSingleton(TechnicalDebtXMLImporter.class);
- servicesContainer.addSingleton(WorkDurationFormatter.class);
servicesContainer.addSingleton(DefaultTechnicalDebtManager.class);
- servicesContainer.addSingleton(WorkDurationFactory.class);
// source
servicesContainer.addSingleton(HtmlSourceDecorator.class);
import org.sonar.api.utils.WorkDuration;
import org.sonar.api.utils.WorkDurationFactory;
import org.sonar.core.technicaldebt.DefaultTechnicalDebtManager;
-import org.sonar.server.ui.WorkDurationFormatter;
import javax.annotation.CheckForNull;
+
import java.util.List;
public class DebtService implements ServerComponent {
- private final WorkDurationFormatter workDurationFormatter;
private final DefaultTechnicalDebtManager finder;
private final WorkDurationFactory workDurationFactory;
- public DebtService(WorkDurationFormatter workDurationFormatter, DefaultTechnicalDebtManager finder, WorkDurationFactory workDurationFactory) {
- this.workDurationFormatter = workDurationFormatter;
+ public DebtService(DefaultTechnicalDebtManager finder, WorkDurationFactory workDurationFactory) {
this.finder = finder;
this.workDurationFactory = workDurationFactory;
}
- public String format(long debt) {
- return workDurationFormatter.format(debt, WorkDurationFormatter.Format.SHORT);
- }
-
public WorkDuration toWorkDuration(long debt) {
return workDurationFactory.createFromMinutes(debt);
}
return i18n.ageFromNow(UserSession.get().locale(), date);
}
+ public String formatWorkDuration(long duration) {
+ return i18n.formatWorkDuration(UserSession.get().locale(), duration);
+ }
+
}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.server.ui;
-
-import org.sonar.api.ServerComponent;
-import org.sonar.api.utils.WorkDuration;
-import org.sonar.api.utils.WorkDurationFactory;
-import org.sonar.core.i18n.DefaultI18n;
-import org.sonar.server.user.UserSession;
-
-import java.util.Locale;
-
-public class WorkDurationFormatter implements ServerComponent {
-
- public enum Format {
- SHORT, LONG
- }
-
- private final DefaultI18n defaultI18n;
- private final WorkDurationFactory workDurationFactory;
-
- public WorkDurationFormatter(DefaultI18n defaultI18n, WorkDurationFactory workDurationFactory) {
- this.defaultI18n = defaultI18n;
- this.workDurationFactory = workDurationFactory;
- }
-
- /**
- * Used by rails
- */
- public String format(long durationInSeconds, String stringFormat) {
- return format(durationInSeconds, Format.valueOf(stringFormat));
- }
-
- public String format(long durationInSeconds, Format format) {
- return formatWorkDuration(UserSession.get().locale(), durationInSeconds, format);
- }
-
- private String formatWorkDuration(Locale locale, long durationInSeconds, Format format) {
- if (durationInSeconds == 0) {
- return "0";
- }
- Long absDuration = Math.abs(durationInSeconds);
- WorkDuration workDuration = workDurationFactory.createFromMinutes(absDuration);
- boolean shortLabel = Format.SHORT.equals(format);
- StringBuilder message = new StringBuilder();
- if (workDuration.days() > 0) {
- message.append(message(locale, "work_duration.x_days", shortLabel, workDuration.days()));
- }
- if (workDuration.hours() > 0) {
- if (message.length() > 0) {
- message.append(" ");
- }
- message.append(message(locale, "work_duration.x_hours", shortLabel, workDuration.hours()));
- }
- if (workDuration.minutes() > 0) {
- if (message.length() > 0) {
- message.append(" ");
- }
- message.append(message(locale, "work_duration.x_minutes", shortLabel, workDuration.minutes()));
- }
- if (durationInSeconds < 0) {
- message.insert(0, "-");
- }
- return message.toString();
- }
-
- private String message(Locale locale, String key, boolean shortLabel, Object... parameters) {
- String msgKey = key;
- if (shortLabel) {
- msgKey += ".short";
- }
- return defaultI18n.message(locale, msgKey, null, parameters);
- }
-}
component(Java::OrgSonarServerUi::JRubyI18n.java_class)
end
- def self.work_duration_formatter
- component(Java::OrgSonarServerUi::WorkDurationFormatter.java_class)
- end
-
private
def self.component(component_java_class)
end
def work_duration_formatted_value(value)
- Internal.work_duration_formatter.format(value.to_i, 'SHORT')
+ Internal.i18n.formatWorkDuration(value.to_i)
end
def color
<% end %>
<% end %>
<% if issue.debt %>
- <li><%= message('issue.debt') -%> <%= Internal.technical_debt.format(issue.debt) -%></li>
+ <li><%= message('issue.debt') -%> <%= Internal.i18n.formatWorkDuration(issue.debt) -%></li>
<% end %>
<% if issue.authorLogin %>
<li><%= message('issue.authorLogin') -%> <%= issue.authorLogin -%></li>
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.i18n.I18n;
import org.sonar.api.issue.internal.FieldDiffs;
-import org.sonar.core.i18n.DefaultI18n;
-import org.sonar.server.ui.WorkDurationFormatter;
import java.util.List;
import java.util.Locale;
import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
private static final Locale DEFAULT_LOCALE = Locale.getDefault();
- private static final int HOURS_IN_DAY = 8;
-
- @Mock
- private DefaultI18n i18n;
-
@Mock
- private WorkDurationFormatter workDurationFormatter;
+ private I18n i18n;
private IssueChangelogFormatter formatter;
@Before
public void before() {
- formatter = new IssueChangelogFormatter(i18n, workDurationFormatter);
+ formatter = new IssueChangelogFormatter(i18n);
}
@Test
FieldDiffs diffs = new FieldDiffs();
diffs.setDiff("technicalDebt", "18000", "28800");
- when(workDurationFormatter.format(18000, WorkDurationFormatter.Format.SHORT)).thenReturn("5 hours");
- when(workDurationFormatter.format(28800, WorkDurationFormatter.Format.SHORT)).thenReturn("1 days");
+ when(i18n.formatWorkDuration(any(Locale.class), eq(18000L))).thenReturn("5 hours");
+ when(i18n.formatWorkDuration(any(Locale.class), eq(28800L))).thenReturn("1 days");
when(i18n.message(DEFAULT_LOCALE, "issue.changelog.field.technicalDebt", null)).thenReturn("Technical Debt");
when(i18n.message(DEFAULT_LOCALE, "issue.changelog.changed_to", null, "Technical Debt", "1 days")).thenReturn("Technical Debt changed to 1 days");
FieldDiffs diffs = new FieldDiffs();
diffs.setDiff("technicalDebt", null, "28800");
- when(workDurationFormatter.format(28800, WorkDurationFormatter.Format.SHORT)).thenReturn("1 days");
+ when(i18n.formatWorkDuration(any(Locale.class), eq(28800L))).thenReturn("1 days");
when(i18n.message(DEFAULT_LOCALE, "issue.changelog.field.technicalDebt", null)).thenReturn("Technical Debt");
when(i18n.message(DEFAULT_LOCALE, "issue.changelog.changed_to", null, "Technical Debt", "1 days")).thenReturn("Technical Debt changed to 1 days");
import org.sonar.server.issue.IssueChangelog;
import org.sonar.server.issue.IssueChangelogService;
import org.sonar.server.issue.IssueService;
-import org.sonar.server.ui.WorkDurationFormatter;
import org.sonar.server.user.MockUserSession;
import org.sonar.server.user.UserSession;
@Mock
ActionService actionService;
- @Mock
- WorkDurationFormatter workDurationFormatter;
-
@Mock
DefaultTechnicalDebtManager technicalDebtManager;
when(i18n.message(any(Locale.class), eq("created"), eq((String) null))).thenReturn("Created");
- tester = new WsTester(new IssuesWs(new IssueShowWsHandler(issueFinder, issueService, issueChangelogService, actionService, workDurationFormatter, technicalDebtManager, i18n)));
+ tester = new WsTester(new IssuesWs(new IssueShowWsHandler(issueFinder, issueService, issueChangelogService, actionService, technicalDebtManager, i18n)));
}
@Test
Issue issue = createStandardIssue().setDebt(technicalDebt);
issues.add(issue);
- when(workDurationFormatter.format(eq(technicalDebt), any(WorkDurationFormatter.Format.class))).thenReturn("2 hours 1 minutes");
+ when(i18n.formatWorkDuration(any(Locale.class), eq(technicalDebt))).thenReturn("2 hours 1 minutes");
MockUserSession.set();
WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
import org.sonar.api.utils.WorkDuration;
import org.sonar.api.utils.WorkDurationFactory;
import org.sonar.core.technicaldebt.DefaultTechnicalDebtManager;
-import org.sonar.server.ui.WorkDurationFormatter;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
public class DebtServiceTest {
private static final int HOURS_IN_DAY = 8;
- WorkDurationFormatter workDurationFormatter = mock(WorkDurationFormatter.class);
DefaultTechnicalDebtManager finder = mock(DefaultTechnicalDebtManager.class);
DebtService service;
public void setUp() throws Exception {
Settings settings = new Settings();
settings.setProperty(CoreProperties.HOURS_IN_DAY, HOURS_IN_DAY);
- service = new DebtService(workDurationFormatter, finder, new WorkDurationFactory(settings));
- }
-
- @Test
- public void format() {
- service.format(10L);
- verify(workDurationFormatter).format(eq(10L), eq(WorkDurationFormatter.Format.SHORT));
+ service = new DebtService(finder, new WorkDurationFactory(settings));
}
@Test
verify(i18n).ageFromNow(any(Locale.class), eq(date));
}
+ @Test
+ public void format_work_duration() throws Exception {
+ jRubyI18n.formatWorkDuration(10L);
+ verify(i18n).formatWorkDuration(any(Locale.class), eq(10L));
+ }
+
}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.server.ui;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.config.Settings;
-import org.sonar.api.utils.WorkDurationFactory;
-import org.sonar.core.i18n.DefaultI18n;
-
-import java.util.Locale;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class WorkDurationFormatterTest {
-
- static final int HOURS_IN_DAY = 8;
-
- static final long ONE_MINUTE = 1L;
- static final long ONE_HOUR = ONE_MINUTE * 60;
- static final long ONE_DAY = HOURS_IN_DAY * ONE_HOUR;
-
- DefaultI18n i18n = mock(DefaultI18n.class);
- WorkDurationFormatter formatter;
-
- @Before
- public void setUp() throws Exception {
- Settings settings = new Settings();
- settings.setProperty(CoreProperties.HOURS_IN_DAY, Integer.toString(HOURS_IN_DAY));
- formatter = new WorkDurationFormatter(i18n, new WorkDurationFactory(settings));
-
- when(i18n.message(any(Locale.class), eq("work_duration.x_days"), eq((String) null), eq(5))).thenReturn("5 days");
- when(i18n.message(any(Locale.class), eq("work_duration.x_days.short"), eq((String) null), eq(5))).thenReturn("5d");
- when(i18n.message(any(Locale.class), eq("work_duration.x_hours"), eq((String) null), eq(2))).thenReturn("2 hours");
- when(i18n.message(any(Locale.class), eq("work_duration.x_hours.short"), eq((String) null), eq(2))).thenReturn("2h");
- when(i18n.message(any(Locale.class), eq("work_duration.x_minutes"), eq((String) null), eq(1))).thenReturn("1 minutes");
- when(i18n.message(any(Locale.class), eq("work_duration.x_minutes.short"), eq((String) null), eq(1))).thenReturn("1min");
- }
-
- @Test
- public void long_format() {
- assertThat(formatter.format(5 * ONE_DAY, WorkDurationFormatter.Format.LONG)).isEqualTo("5 days");
- assertThat(formatter.format(2 * ONE_HOUR, WorkDurationFormatter.Format.LONG)).isEqualTo("2 hours");
- assertThat(formatter.format(ONE_MINUTE, WorkDurationFormatter.Format.LONG)).isEqualTo("1 minutes");
-
- assertThat(formatter.format(5 * ONE_DAY + 2 * ONE_HOUR, WorkDurationFormatter.Format.LONG)).isEqualTo("5 days 2 hours");
- assertThat(formatter.format(2 * ONE_HOUR + ONE_MINUTE, WorkDurationFormatter.Format.LONG)).isEqualTo("2 hours 1 minutes");
-
- assertThat(formatter.format(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, WorkDurationFormatter.Format.LONG)).isEqualTo("5 days 2 hours 1 minutes");
- }
-
- @Test
- public void short_format() {
- assertThat(formatter.format(5 * ONE_DAY, WorkDurationFormatter.Format.SHORT)).isEqualTo("5d");
- assertThat(formatter.format(2 * ONE_HOUR, WorkDurationFormatter.Format.SHORT)).isEqualTo("2h");
- assertThat(formatter.format(ONE_MINUTE, WorkDurationFormatter.Format.SHORT)).isEqualTo("1min");
-
- assertThat(formatter.format(5 * ONE_DAY + 2 * ONE_HOUR, WorkDurationFormatter.Format.SHORT)).isEqualTo("5d 2h");
- assertThat(formatter.format(2 * ONE_HOUR + ONE_MINUTE, WorkDurationFormatter.Format.SHORT)).isEqualTo("2h 1min");
-
- assertThat(formatter.format(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, WorkDurationFormatter.Format.SHORT)).isEqualTo("5d 2h 1min");
- }
-
- @Test
- public void format_with_string_parameter() {
- assertThat(formatter.format(5 * ONE_DAY, "LONG")).isEqualTo("5 days");
- assertThat(formatter.format(5 * ONE_DAY, "SHORT")).isEqualTo("5d");
- }
-
- @Test
- public void display_zero_without_unit() {
- assertThat(formatter.format(0, WorkDurationFormatter.Format.SHORT)).isEqualTo("0");
- }
-
- @Test
- public void display_negative_duration() {
- assertThat(formatter.format(-5 * ONE_DAY, WorkDurationFormatter.Format.SHORT)).isEqualTo("-5d");
- assertThat(formatter.format(-2 * ONE_HOUR, WorkDurationFormatter.Format.SHORT)).isEqualTo("-2h");
- assertThat(formatter.format(-1 * ONE_MINUTE, WorkDurationFormatter.Format.SHORT)).isEqualTo("-1min");
- }
-
-}