package hudson.plugins.analysis.core;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.StaplerProxy;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jenkins.model.Jenkins;
import jenkins.tasks.SimpleBuildStep.LastBuildAction;
import hudson.maven.MavenBuild;
import hudson.maven.MavenModule;
import hudson.model.AbstractBuild;
import hudson.model.Action;
import hudson.model.HealthReport;
import hudson.model.HealthReportingAction;
import hudson.model.Job;
import hudson.model.Run;
import hudson.plugins.analysis.Messages;
import hudson.plugins.analysis.util.ToolTipProvider;
/**
* Controls the live cycle of the results in a job. This action persists the results
* of a build and displays them on the build page. The actual visualization of
* the results is defined in the matching <code>summary.jelly</code> file.
* <p>
* Moreover, this class renders the results trend.
* </p>
*
* @param <T>
* type of the result of this action
* @author Ulli Hafner
*/
//CHECKSTYLE:COUPLING-OFF
@ExportedBean
public abstract class AbstractResultAction<T extends BuildResult> implements StaplerProxy, HealthReportingAction, ToolTipProvider, ResultAction<T>, LastBuildAction {
/** The associated run of this action. */
private final Run<?, ?> owner;
/** Parameters for the health report. */
private final AbstractHealthDescriptor healthDescriptor;
/** The actual result of this action. */
private T result;
/**
* Creates a new instance of <code>AbstractResultAction</code>.
*
* @param owner
* the associated build of this action
* @param healthDescriptor
* health descriptor
* @param result
* the result of the action
*/
public AbstractResultAction(final Run<?, ?> owner, final AbstractHealthDescriptor healthDescriptor, final T result) {
this.owner = owner;
this.result = result;
this.healthDescriptor = healthDescriptor;
}
/**
* Wraps the specified action into a set.
*
* @param action the action to add to the set
* @return a set containing the action
*/
protected Set<Action> asSet(final Action action) {
return Collections.singleton(action);
}
/**
* Returns the healthDescriptor.
*
* @return the healthDescriptor
*/
@Override
public AbstractHealthDescriptor getHealthDescriptor() {
if (healthDescriptor == null) {
return NullHealthDescriptor.NULL_HEALTH_DESCRIPTOR; // for old serialized actions
}
else {
return healthDescriptor;
}
}
/**
* Returns the descriptor of the associated plug-in.
*
* @return the descriptor of the associated plug-in
*/
protected abstract PluginDescriptor getDescriptor();
@Exported
public String getName() {
return getDescriptor().getPluginName();
}
protected Job<?, ?> getJob() {
return getBuild().getParent();
}
@Override
public String getUrlName() {
return getDescriptor().getPluginResultUrlName();
}
@Override @Exported
public final HealthReport getBuildHealth() {
return new HealthReportBuilder(getHealthDescriptor()).computeHealth(getResult());
}
@Override
public ToolTipProvider getToolTipProvider() {
return this;
}
/**
* Returns the associated build of this action.
*
* @return the associated build of this action
*/
@WithBridgeMethods(value=AbstractBuild.class, adapterMethod="getAbstractBuild")
public final Run<?, ?> getOwner() {
return owner;
}
@Override
@WithBridgeMethods(value=AbstractBuild.class, adapterMethod="getAbstractBuild")
public final Run<?, ?> getBuild() {
return owner;
}
/**
* Returns the project actions for this result action.
*
* @return default implementation returns empty collection, plug-in must override if they want to contribute to the UI.
* FIXME: Make it abstract in 2.0
*/
@Override
public Collection<? extends Action> getProjectActions() {
return Collections.emptyList();
}
/**
* Added for backward compatibility. It generates <pre>AbstractBuild getOwner()</pre> bytecode during the build
* process, so old implementations can use that signature.
*
* @see {@link WithBridgeMethods}
*/
@Deprecated
private Object getAbstractBuild(final Run owner, final Class targetClass) {
return owner instanceof AbstractBuild ? (AbstractBuild) owner : null;
}
@Override
public final Object getTarget() {
return getResult();
}
@Override @Exported
public final T getResult() {
return result;
}
@Override
public final void setResult(final T result) {
this.result = result;
}
@Override
public String getIconFileName() {
T currentResult = getResult();
if (currentResult != null && currentResult.getNumberOfAnnotations() > 0) {
return getSmallImage();
}
return null;
}
/**
* Returns whether a large image is defined.
*
* @return <code>true</code> if a large image is defined, <code>false</code>
* otherwise. If no large image is defined, then the attribute
* {@code icon} must to be provided in jelly tag {@code summary}.
* @since 1.41
*/
public boolean hasLargeImage() {
return StringUtils.isNotBlank(getLargeImageName());
}
/**
* Returns the URL of the 48x48 image used in the build summary.
*
* @return the URL of the image
* @since 1.41
*/
public String getLargeImageName() {
return getDescriptor().getSummaryIconUrl();
}
/**
* Returns the URL of the 24x24 image used in the build link.
*
* @return the URL of the image
* @since 1.41
*/
public String getSmallImageName() {
return getSmallImage();
}
/**
* Returns the URL of the 24x24 image used in the build link.
*
* @return the URL of the image
*/
protected String getSmallImage() {
return createStaticIconUrl(getDescriptor().getIconUrl());
}
private String createStaticIconUrl(final String iconUrl) {
return Jenkins.RESOURCE_PATH + "/" + iconUrl;
}
/**
* Factory method to create the result of this action.
*
* @return the result of this action
*/
protected ParserResult createResult() {
return new ParserResult();
}
@Override
public String getTooltip(final int numberOfItems) {
if (numberOfItems == 1) {
return getSingleItemTooltip();
}
else {
return getMultipleItemsTooltip(numberOfItems);
}
}
/**
* Returns the tooltip for several items.
*
* @param numberOfItems
* the number of items to display the tooltip for
* @return the tooltip for several items
*/
protected String getMultipleItemsTooltip(final int numberOfItems) {
return Messages.ResultAction_MultipleWarnings(numberOfItems);
}
/**
* Returns the tooltip for exactly one item.
*
* @return the tooltip for exactly one item
*/
protected String getSingleItemTooltip() {
return Messages.ResultAction_OneWarning();
}
@Override @Exported
public boolean isSuccessful() {
return getResult().isSuccessful();
}
/**
* Aggregates the results of the specified maven module builds.
*
* @param moduleBuilds
* the module builds to aggregate
* @return the aggregated result
* @deprecated replaced by {@link MavenResultAction}
*/
@Deprecated
protected ParserResult createAggregatedResult(final Map<MavenModule, List<MavenBuild>> moduleBuilds) {
return new ParserResult();
}
/**
* Updates the build status if the number of annotations exceeds one of the
* thresholds.
*
* @param build
* the build to change the status from
* @param buildResult
* the build result
* @deprecated replaced by {@link MavenResultAction}
*/
@Deprecated
protected void updateBuildHealth(final MavenBuild build, final BuildResult buildResult) {
// does nothing
}
/**
* Adds a new module to the specified project. The new module is obtained
* from the specified list of builds.
*
* @param aggregatedResult
* the result to add the module to
* @param builds
* the builds for a module
* @deprecated replaced by {@link MavenResultAction}
*/
@Deprecated
protected void addModule(final ParserResult aggregatedResult, final List<MavenBuild> builds) {
// does nothing
}
/**
* Creates a new instance of <code>AbstractResultAction</code>.
*
* @param owner
* the associated build of this action
* @param healthDescriptor
* health descriptor
* @deprecated use
* {@link #AbstractResultAction(AbstractBuild, AbstractHealthDescriptor, BuildResult)}
* so that every action will have a result that is not null
*/
@Deprecated
public AbstractResultAction(final AbstractBuild<?, ?> owner, final AbstractHealthDescriptor healthDescriptor) {
this.owner = owner;
this.healthDescriptor = healthDescriptor;
}
/**
* Creates a new instance of <code>AbstractResultAction</code>.
*
* @param owner
* the associated build of this action
* @param healthDescriptor
* health descriptor
* @param result
* the result of the action
* @deprecated use {@link #AbstractResultAction(Run, AbstractHealthDescriptor, BuildResult)} instead
*/
@Deprecated
public AbstractResultAction(final AbstractBuild<?, ?> owner, final AbstractHealthDescriptor healthDescriptor, final T result) {
this.owner = owner;
this.result = result;
this.healthDescriptor = healthDescriptor;
}
/** Backward compatibility. @deprecated */
@Deprecated
@java.lang.SuppressWarnings("PMD")
@SuppressFBWarnings("UuF")
private transient HealthReportBuilder healthReportBuilder; // NOPMD
}