package hudson.model;
import hudson.Extension;
import hudson.Util;
import hudson.model.Descriptor.FormException;
import hudson.util.FormValidation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.servlet.ServletException;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
/**
* A configurable Radiator-Style job view suitable for use in extreme feedback
* systems - ideal for running on a spare PC in the office. Many thanks to
* Julien Renaut for the xfpanel plugin that inspired some of the updates to
* this view.
*
* @author Mark Howard (mh@tildemh.com)
*/
public class RadiatorView extends ListView
{
/**
* Entries to be shown in the view.
*/
private transient Collection<ViewEntry> entries;
/**
* Cache of location of jobs in the build queue.
*/
transient Map<hudson.model.Queue.Item, Integer> placeInQueue = new HashMap<hudson.model.Queue.Item, Integer>();
/**
* Colours to use in the view.
*/
transient ViewEntryColors colors;
/**
* User configuration - show stable builds when there are some unstable
* builds.
*/
private Boolean showStable = false;
/**
* User configuration - show details in stable builds.
*/
private Boolean showStableDetail = false;
/**
* User configuration - high visibility mode.
*/
private Boolean highVis = true;
/**
* @param name
* view name.
* @param showStable
* if stable builds should be shown.
* @param showStableDetail
* if detail should be shown for stable builds.
* @param highVis
* high visibility mode.
*/
@DataBoundConstructor
public RadiatorView(String name, Boolean showStable, Boolean showStableDetail, Boolean highVis)
{
super(name);
this.showStable = showStable;
this.showStableDetail = showStableDetail;
this.highVis = highVis;
}
/**
* @return the colors to use
*/
public ViewEntryColors getColors()
{
if (this.colors == null)
{
this.colors = ViewEntryColors.DEFAULT;
}
return this.colors;
}
public RadiatorViewContents getContents()
{
RadiatorViewContents contents = new RadiatorViewContents();
placeInQueue = new HashMap<hudson.model.Queue.Item, Integer>();
int j = 1;
for (hudson.model.Queue.Item i : Hudson.getInstance().getQueue().getItems())
{
placeInQueue.put(i, j++);
}
for (TopLevelItem item : super.getItems())
{
if (item instanceof AbstractProject)
{
AbstractProject project = (AbstractProject) item;
if (!project.isDisabled())
{
ViewEntry entry = new ViewEntry(this, project);
if (getResult(project).isBetterOrEqualTo(Result.SUCCESS))
{
contents.addPassingBuild(entry);
}
else if ("Not Claimed.".equals(entry.getClaim()))
{
contents.addFailingBuild(entry);
}
else
{
contents.addClaimedBuild(entry);
}
}
}
}
return contents;
}
/**
* Gets from the request the configuration parameters
*
* @param req
* {@link StaplerRequest}
* @throws ServletException
* if any
* @throws FormException
* if any
*/
@Override
protected void submit(StaplerRequest req) throws ServletException, FormException
{
super.submit(req);
this.showStable = Boolean.parseBoolean(req.getParameter("showStable"));
this.showStableDetail = Boolean.parseBoolean(req.getParameter("showStableDetail"));
this.highVis = Boolean.parseBoolean(req.getParameter("highVis"));
}
public Boolean getShowStable()
{
return showStable;
}
public Boolean getShowStableDetail()
{
return showStableDetail;
}
public Boolean getHighVis()
{
return highVis;
}
/**
* Converts a list of jobs to a list of list of jobs, suitable for display
* as rows in a table.
*
* @param jobs
* the jobs to include.
* @param failingJobs
* if this is a list of failing jobs, in which case fewer jobs
* should be used per row.
* @return a list of fixed size view entry lists.
*/
public Collection<Collection<ViewEntry>> toRows(Collection<ViewEntry> jobs, Boolean failingJobs)
{
int jobsPerRow = 1;
if (failingJobs.booleanValue())
{
if (jobs.size() > 3)
{
jobsPerRow = 2;
}
if (jobs.size() > 9)
{
jobsPerRow = 3;
}
if (jobs.size() > 15)
{
jobsPerRow = 4;
}
}
else
{
// don't mind having more rows as much for passing jobs.
jobsPerRow = (int) Math.floor(Math.sqrt(jobs.size()) / 1.5);
}
Collection<Collection<ViewEntry>> rows = new ArrayList<Collection<ViewEntry>>();
Collection<ViewEntry> current = null;
int i = 0;
for (ViewEntry job : jobs)
{
if (i == 0)
{
current = new ArrayList<ViewEntry>();
rows.add(current);
}
current.add(job);
i++;
if (i >= jobsPerRow)
{
i = 0;
}
}
return rows;
}
public static Result getResult(Job job)
{
Run lastBuild = job.getLastBuild();
while (lastBuild != null
&& (lastBuild.hasntStartedYet() || lastBuild.isBuilding() || lastBuild
.isLogUpdated()))
{
lastBuild = lastBuild.getPreviousBuild();
}
if (lastBuild != null)
{
return lastBuild.getResult();
}
else
{
return Result.NOT_BUILT;
}
}
@Extension
public static final class DescriptorImpl extends ViewDescriptor
{
public DescriptorImpl()
{
super();
}
@Override
public String getDisplayName()
{
return "Radiator";
}
/**
* Checks if the include regular expression is valid.
*/
public FormValidation doCheckIncludeRegex(@QueryParameter String value)
{
String v = Util.fixEmpty(value);
if (v != null)
{
try
{
Pattern.compile(v);
}
catch (PatternSyntaxException pse)
{
return FormValidation.error(pse.getMessage());
}
}
return FormValidation.ok();
}
}
}