/******************************************************************************* * * Copyright (c) 2004-2010 Oracle Corporation. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * Kohsuke Kawaguchi, Stephen Connolly * * *******************************************************************************/ package hudson.model; import com.thoughtworks.xstream.converters.UnmarshallingContext; import hudson.diagnosis.OldDataMonitor; import hudson.util.XStream2; import org.jvnet.localizer.Localizable; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; import java.io.*; import java.util.Collections; import java.util.List; import java.util.Locale; /** * Represents health of something (typically project). A number between 0-100. * * @author connollys * @since 1.115 */ @ExportedBean(defaultVisibility = 2) // this is always exported as a part of Job and never on its own, so start with 2. public class HealthReport implements Serializable, Comparable<HealthReport> { // These are now 0-20, 21-40, 41-60, 61-80, 81+ but filenames unchanged for compatibility private static final String HEALTH_OVER_80 = "health-80plus.png"; private static final String HEALTH_61_TO_80 = "health-60to79.png"; private static final String HEALTH_41_TO_60 = "health-40to59.png"; private static final String HEALTH_21_TO_40 = "health-20to39.png"; private static final String HEALTH_0_TO_20 = "health-00to19.png"; private static final String HEALTH_UNKNOWN = "empty.png"; /** * The percentage health score (from 0 to 100 inclusive). */ private int score; /** * The path to the icon corresponding to this health score or * <code>null</code> to use the default icon corresponding to the current * health score. * <p/> * If the path begins with a '/' then it will be the absolute path, * otherwise the image is assumed to be in one of * <code>/images/16x16/</code>, * <code>/images/24x24/</code> or * <code>/images/32x32/</code> depending on the icon size selected by the * user. */ private String iconUrl; /** * Recover the health icon's tool-tip when deserializing. * * @deprecated since 2008-10-18. Use {@link #localizibleDescription} */ @Deprecated private transient String description; /** * The health icon's tool-tip. */ private Localizable localizibleDescription; /** * Create a new HealthReport. * * @param score The percentage health score (from 0 to 100 inclusive). * @param iconUrl The path to the icon corresponding to this * {@link Action}'s health or <code>null</code> to display the default icon * corresponding to the current health score. * <p/> * If the path begins with a '/' then it will be the absolute path, * otherwise the image is assumed to be in one * of <code>/images/16x16/</code>, <code>/images/24x24/</code> or * <code>/images/32x32/</code> depending on the icon size selected by the * user. When calculating the url to display for absolute paths, the * getIconUrl(String) method will replace /32x32/ in the path with the * appropriate size. * @param description The health icon's tool-tip. * @deprecated since 2008-10-18. Use * {@link #HealthReport(int, String, org.jvnet.localizer.Localizable)} */ @Deprecated public HealthReport(int score, String iconUrl, String description) { this(score, iconUrl, new NonLocalizable(description)); } /** * Create a new HealthReport. * * @param score The percentage health score (from 0 to 100 inclusive). * @param iconUrl The path to the icon corresponding to this * {@link Action}'s health or <code>null</code> to display the default icon * corresponding to the current health score. * <p/> * If the path begins with a '/' then it will be the absolute path, * otherwise the image is assumed to be in one * of <code>/images/16x16/</code>, <code>/images/24x24/</code> or * <code>/images/32x32/</code> depending on the icon size selected by the * user. When calculating the url to display for absolute paths, the * getIconUrl(String) method will replace /32x32/ in the path with the * appropriate size. * @param description The health icon's tool-tip. */ public HealthReport(int score, String iconUrl, Localizable description) { this.score = score; if (iconUrl == null) { if (score <= 20) { this.iconUrl = HEALTH_0_TO_20; } else if (score <= 40) { this.iconUrl = HEALTH_21_TO_40; } else if (score <= 60) { this.iconUrl = HEALTH_41_TO_60; } else if (score <= 80) { this.iconUrl = HEALTH_61_TO_80; } else { this.iconUrl = HEALTH_OVER_80; } } else { this.iconUrl = iconUrl; } this.description = null; setLocalizibleDescription(description); } /** * Create a new HealthReport. * * @param score The percentage health score (from 0 to 100 inclusive). * @param description The health icon's tool-tip. * @deprecated since 2008-10-18. Use * {@link #HealthReport(int, org.jvnet.localizer.Localizable)} */ @Deprecated public HealthReport(int score, String description) { this(score, null, description); } /** * Create a new HealthReport. * * @param score The percentage health score (from 0 to 100 inclusive). * @param description The health icon's tool-tip. */ public HealthReport(int score, Localizable description) { this(score, null, description); } /** * Create a new HealthReport. */ public HealthReport() { this(100, HEALTH_UNKNOWN, Messages._HealthReport_EmptyString()); } /** * Getter for property 'score'. * * @return The percentage health score (from 0 to 100 inclusive). */ @Exported public int getScore() { return score; } /** * Setter for property 'score'. * * @param score Value to set for property 'score'. */ public void setScore(int score) { this.score = score; } /** * Getter for property 'iconUrl'. * * @return Value for property 'iconUrl'. */ @Exported public String getIconUrl() { return iconUrl; } /** * Get's the iconUrl relative to the hudson root url, for the correct size. * * @param size The size, e.g. 32x32, 24x24 or 16x16. * @return The url relative to hudson's root url. */ public String getIconUrl(String size) { if (iconUrl == null) { return Hudson.RESOURCE_PATH + "/images/" + size + "/" + HEALTH_UNKNOWN; } if (iconUrl.startsWith("/")) { return iconUrl.replace("/32x32/", "/" + size + "/"); } return Hudson.RESOURCE_PATH + "/images/" + size + "/" + iconUrl; } /** * Setter for property 'iconUrl'. * * @param iconUrl Value to set for property 'iconUrl'. */ public void setIconUrl(String iconUrl) { this.iconUrl = iconUrl; } /** * Getter for property 'description'. * * @return Value for property 'description'. */ @Exported public String getDescription() { return getLocalizableDescription().toString(); } /** * Setter for property 'description'. * * @param description Value to set for property 'description'. */ public void setDescription(String description) { setLocalizibleDescription(new NonLocalizable(description)); } /** * Getter for property 'localizibleDescription'. * * @return Value for property 'localizibleDescription'. */ public Localizable getLocalizableDescription() { return localizibleDescription; } /** * Setter for property 'localizibleDescription'. * * @param localizibleDescription Value to set for property * 'localizibleDescription'. */ public void setLocalizibleDescription(Localizable localizibleDescription) { this.localizibleDescription = localizibleDescription; } /** * Getter for property 'aggregatedReports'. * * @return Value for property 'aggregatedReports'. */ public List<HealthReport> getAggregatedReports() { return Collections.emptyList(); } /** * Getter for property 'aggregateReport'. * * @return Value for property 'aggregateReport'. */ public boolean isAggregateReport() { return false; } /** * {@inheritDoc} */ public int compareTo(HealthReport o) { return (this.score < o.score ? -1 : (this.score == o.score ? 0 : 1)); } /** * Utility method to find the report with the lowest health. */ public static HealthReport min(HealthReport a, HealthReport b) { if (a == null && b == null) { return null; } if (a == null) { return b; } if (b == null) { return a; } if (a.compareTo(b) <= 0) { return a; } return b; } /** * Utility method to find the report with the highest health. */ public static HealthReport max(HealthReport a, HealthReport b) { if (a == null && b == null) { return null; } if (a == null) { return b; } if (b == null) { return a; } if (a.compareTo(b) >= 0) { return a; } return b; } /** * Fix deserialization of older data. */ public static class ConverterImpl extends XStream2.PassthruConverter<HealthReport> { public ConverterImpl(XStream2 xstream) { super(xstream); } @Override protected void callback(HealthReport hr, UnmarshallingContext context) { // If we are being read back in from an older version if (hr.localizibleDescription == null) { hr.localizibleDescription = new NonLocalizable(hr.description == null ? "" : hr.description); OldDataMonitor.report(context, "1.256"); } } } /** * In order to provide backwards compatibility, we use this crazy class to * fake out localization. */ private static class NonLocalizable extends Localizable { /** * The string that we don't know how to localize */ private final String nonLocalizable; /** * Creates a non-localizable string. * * @param nonLocalizable the string. */ public NonLocalizable(String nonLocalizable) { super(null, null); this.nonLocalizable = nonLocalizable; } /** * {@inheritDoc} */ @Override public String toString(Locale locale) { return nonLocalizable; } /** * {@inheritDoc} */ @Override public String toString() { return nonLocalizable; } } }